home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / msdos / fractal / frasr182 / hc.c < prev    next >
C/C++ Source or Header  |  1993-04-16  |  87KB  |  3,928 lines

  1.  
  2. /*
  3.  * hc.c
  4.  *
  5.  * Stand-alone FRACTINT help compiler.  Compile in the COMPACT memory model.
  6.  *
  7.  * See HC.DOC for source file syntax.
  8.  *
  9.  *
  10.  * Revision History:
  11.  *
  12.  *   02-26-91 EAN     Initial version.
  13.  *
  14.  *   03-21-91 EAN     Modified for automatic paragraph formatting.
  15.  *              Added several new commands:
  16.  *             Format[+/-]  Enable/disable paragraph formatting
  17.  *             Doc[+/-]     Enable/disable output to document.
  18.  *             Online[+/-]  Enable/disable output to online help.
  19.  *             Label=       Defines a label. Replaces ~(...)
  20.  *             FF          Forces a form-feed.  Replaces ~~
  21.  *             FormatExclude=val Exclude lines past val from
  22.  *                      formatting.  If before any topic sets
  23.  *                      global default, otherwise set local.
  24.  *             FormatExclude= Set to global default.
  25.  *             FormatExclude=n Disable exclusion. (global or local)
  26.  *             FormatExclude[+/-] Enable/disable format exclusion.
  27.  *             Center[+/-]  Enable/disable centering of text.
  28.  *             \ before nl  Forces the end of a paragraph
  29.  *              Support for commands embedded in text with new
  30.  *              ~(...) format.
  31.  *              Support for multiple commands on a line separated by
  32.  *              commas.
  33.  *              Support for implict links; explicit links must now
  34.  *              start with an equal sign.
  35.  *   04-03-91 EAN     Added "include" command (works like #include)
  36.  *   04-10-91 EAN     Added support for "data" topics.
  37.  *              Added Comment/EndComment commands for multi-line
  38.  *             comments.
  39.  *              Added CompressSpaces[+/-] command.
  40.  *              Added DocContents command for document printing.
  41.  *              Added BinInc command which includes a binary file
  42.  *             in a data topic.
  43.  *              Fixed tables to flow down instead of across the page.
  44.  *             Makes no allowances for page breaks within tables.
  45.  *
  46.  */
  47.  
  48.  
  49. #define HC_C
  50.  
  51. #define INCLUDE_COMMON  /* tell helpcom.h to include common code */
  52.  
  53.  
  54. #include <stdio.h>
  55. #ifndef XFRACT
  56. #include <io.h>
  57. #include <stdarg.h>
  58. #else
  59. #include <varargs.h>
  60. #define strupr strlwr
  61. #endif
  62. #include <fcntl.h>
  63. #include <stdlib.h>
  64. #include <string.h>
  65. #include <ctype.h>
  66.  
  67. #ifdef __TURBOC__
  68. #   include <dir.h>
  69. #   define FNSPLIT fnsplit
  70. #else
  71. #   define MAXFILE _MAX_FNAME
  72. #   define MAXEXT  _MAX_EXT
  73. #   define FNSPLIT _splitpath
  74. #endif
  75.  
  76.  
  77. #include <assert.h>
  78. #include "helpcom.h"
  79.  
  80.  
  81. /*
  82.  * When defined, SHOW_ERROR_LINE will cause the line number in HC.C where
  83.  * errors/warnings/messages are generated to be displayed at the start of
  84.  * the line.
  85.  *
  86.  * Used when debugging HC.  Also useful for finding the line (in HC.C) that
  87.  * generated a error or warning.
  88.  */
  89.  
  90. #ifndef XFRACT
  91. #define SHOW_ERROR_LINE
  92. #endif
  93.  
  94.  
  95. #define DEFAULT_SRC_FNAME "help.src"
  96. #define DEFAULT_HLP_FNAME "fractint.hlp"
  97. #define DEFAULT_EXE_FNAME "fractint.exe"
  98. #define DEFAULT_DOC_FNAME "fractint.doc"
  99.  
  100. #define TEMP_FNAME      "HC.$$$"
  101. #define SWAP_FNAME      "HCSWAP.$$$"
  102.  
  103. #define MAX_ERRORS      (25)     /* stop after this many errors */
  104. #define MAX_WARNINGS      (25)     /* stop after this many warnings */
  105.                  /* 0 = never stop */
  106.  
  107. #define INDEX_LABEL      "HELP_INDEX"
  108. #define DOCCONTENTS_TITLE "DocContent"
  109.  
  110.  
  111.  
  112. #define BUFFER_SIZE   (24*1024)
  113.  
  114.  
  115. typedef struct
  116.    {
  117.    int        type;         /* 0 = name is topic title, 1 = name is label, */
  118.                  /*   2 = "special topic"; name is NULL and */
  119.                  /*   topic_num/topic_off is valid */
  120.    int        topic_num;         /* topic number to link to */
  121.    unsigned topic_off;         /* offset into topic to link to */
  122.    int        doc_page;         /* document page # to link to */
  123.    char    *name;         /* name of label or title of topic to link to */
  124.    char    *srcfile;         /* .SRC file link appears in */
  125.    int        srcline;         /* .SRC file line # link appears in */
  126.    } LINK;
  127.  
  128.  
  129. typedef struct
  130.    {
  131.    unsigned offset;     /* offset from start of topic text */
  132.    unsigned length;     /* length of page (in chars) */
  133.    int        margin;     /* if > 0 then page starts in_para and text */
  134.                 /* should be indented by this much */
  135.    } PAGE;
  136.  
  137.  
  138. /* values for TOPIC.flags */
  139.  
  140. #define TF_IN_DOC  (1)         /* 1 if topic is part of the printed document */
  141. #define TF_DATA    (2)         /* 1 if it is a "data" topic */
  142.  
  143.  
  144. typedef struct
  145.    {
  146.    unsigned  flags;         /* see #defines for TF_??? */
  147.    int         doc_page;         /* page number in document where topic starts */
  148.    unsigned  title_len;      /* length of title */
  149.    char     *title;         /* title for this topic */
  150.    int         num_page;         /* number of pages */
  151.    PAGE     *page;         /* list of pages */
  152.    unsigned  text_len;         /* lenth of topic text */
  153.    long      text;         /* topic text (all pages) */
  154.    long      offset;         /* offset to topic from start of file */
  155.    } TOPIC;
  156.  
  157.  
  158. typedef struct
  159.    {
  160.    char    *name;         /* its name */
  161.    int        topic_num;         /* topic number */
  162.    unsigned topic_off;         /* offset of label in the topic's text */
  163.    int        doc_page;
  164.    } LABEL;
  165.  
  166.  
  167. /* values for CONTENT.flags */
  168.  
  169. #define CF_NEW_PAGE  (1)     /* true if section starts on a new page */
  170.  
  171.  
  172. #define MAX_CONTENT_TOPIC (10)
  173.  
  174.  
  175. typedef struct
  176.    {
  177.    unsigned  flags;
  178.    char     *id;
  179.    char     *name;
  180.    int         doc_page;
  181.    unsigned  page_num_pos;
  182.    int         num_topic;
  183.    char      is_label[MAX_CONTENT_TOPIC];
  184.    char     *topic_name[MAX_CONTENT_TOPIC];
  185.    int         topic_num[MAX_CONTENT_TOPIC];
  186.    char     *srcfile;
  187.    int         srcline;
  188.    } CONTENT;
  189.  
  190.  
  191. struct help_sig_info
  192.    {
  193.    unsigned long sig;
  194.    int         version;
  195.    unsigned long base;
  196.    } ;
  197.  
  198.  
  199. int     num_topic      = 0;      /* topics */
  200. TOPIC   *topic;
  201.  
  202. int     num_label      = 0;      /* labels */
  203. LABEL   *label;
  204.  
  205. int     num_plabel      = 0;      /* private labels */
  206. LABEL   *plabel;
  207.  
  208. int     num_link      = 0;      /* all links */
  209. LINK    *a_link          = 0;
  210.  
  211. int     num_contents      = 0;      /* the table-of-contents */
  212. CONTENT *contents;
  213.  
  214. int     quiet_mode      = 0;      /* true if "/Q" option used */
  215.  
  216. int     max_pages      = 0;      /* max. pages in any topic */
  217. int     max_links      = 0;      /* max. links on any page */
  218. int     num_doc_pages      = 0;      /* total number of pages in document */
  219.  
  220. FILE    *srcfile;          /* .SRC file */
  221. int     srcline      = 0;      /* .SRC line number (used for errors) */
  222. int     srccol       = 0;      /* .SRC column. */
  223.  
  224. int     version      = -1;   /* help file version */
  225.  
  226. int     errors       = 0,      /* number of errors reported */
  227.      warnings      = 0;      /* number of warnings reported */
  228.  
  229. char     src_fname[81]      = "";   /* command-line .SRC filename */
  230. char     hdr_fname[81]      = "";   /* .H filename */
  231. char     hlp_fname[81]      = "";   /* .HLP filename */
  232. char    *src_cfname      = NULL; /* current .SRC filename */
  233.  
  234. int     format_exclude   = 0;      /* disable formatting at this col, 0 to */
  235.                   /*    never disable formatting */
  236. FILE    *swapfile;
  237. long     swappos;
  238.  
  239. char    *buffer;          /* alloc'ed as BUFFER_SIZE bytes */
  240. char    *curr;              /* current position in the buffer */
  241. char     cmd[128];          /* holds the current command */
  242. int     compress_spaces;
  243. int     xonline;
  244. int     xdoc;
  245.  
  246. #define  MAX_INCLUDE_STACK (5)      /* allow 5 nested includes */
  247.  
  248. struct
  249.    {
  250.    char *fname;
  251.    FILE *file;
  252.    int     line;
  253.    int     col;
  254.    } include_stack[MAX_INCLUDE_STACK];
  255. int include_stack_top = -1;
  256.  
  257.  
  258. #define CHK_BUFFER(off) { if ((unsigned)(curr+(off)) - (unsigned)buffer >= (BUFFER_SIZE-1024)) fatal(0,"Buffer overflowed -- Help topic too large."); }
  259.  
  260. #ifdef __WATCOMC__
  261. #define putw( x1, x2 )  fprintf( x2, "%c%c", x1&0xFF, x1>>8 );
  262. #endif
  263.  
  264. #ifdef XFRACT
  265. #define putw( x1, x2 )  fwrite( &(x1), 1, sizeof(int), x2);
  266. #endif
  267.  
  268. /*
  269.  * error/warning/message reporting functions.
  270.  */
  271.  
  272.  
  273. void report_errors(void)
  274.    {
  275.    printf("\n");
  276.    printf("Compiler Status:\n");
  277.    printf("%8d Error%c\n",     errors,   (errors==1)     ? ' ' : 's');
  278.    printf("%8d Warning%c\n",     warnings, (warnings==1) ? ' ' : 's');
  279.    }
  280.  
  281.  
  282. void print_msg(char *type, int lnum, char *format, va_list arg)
  283.    {
  284.    if (type != NULL)
  285.       {
  286.       printf("   %s", type);
  287.       if (lnum>0)
  288.      printf(" %s %d", src_cfname, lnum);
  289.       printf(": ");
  290.       }
  291.    vprintf(format, arg);
  292.    printf("\n");
  293.    }
  294.  
  295.  
  296. #ifndef XFRACT
  297. void fatal(int diff, char *format, ...)
  298. #else
  299. void fatal(va_alist)
  300.     va_dcl
  301. #endif
  302.    {
  303.    va_list arg;
  304.  
  305. #ifndef XFRACT
  306.    va_start(arg, format);
  307. #else
  308.    int diff;
  309.    char *format;
  310.    va_start(arg);
  311.    diff = va_arg(arg,int);
  312.    format = va_arg(arg,char *);
  313. #endif
  314.  
  315.    print_msg("Fatal", srcline-diff, format, arg);
  316.    va_end(arg);
  317.  
  318.    if ( errors || warnings )
  319.       report_errors();
  320.  
  321.    exit( errors + 1 );
  322.    }
  323.  
  324.  
  325. #ifndef XFRACT
  326. void error(int diff, char *format, ...)
  327. #else
  328. void error(va_alist)
  329.     va_dcl
  330. #endif
  331.    {
  332.    va_list arg;
  333.  
  334. #ifndef XFRACT
  335.    va_start(arg, format);
  336. #else
  337.    int diff;
  338.    char *format;
  339.    va_start(arg);
  340.    diff = va_arg(arg,int);
  341.    format = va_arg(arg,char *);
  342. #endif
  343.    print_msg("Error", srcline-diff, format, arg);
  344.    va_end(arg);
  345.  
  346.    if (++errors >= MAX_ERRORS && MAX_ERRORS > 0)
  347.       fatal(0,"Too many errors!");
  348.    }
  349.  
  350.  
  351. #ifndef XFRACT
  352. void warn(int diff, char *format, ...)
  353. #else
  354. void warn(va_alist)
  355.    va_dcl
  356. #endif
  357.    {
  358.    va_list arg;
  359. #ifndef XFRACT
  360.    va_start(arg, format);
  361. #else
  362.    int diff;
  363.    char *format;
  364.    va_start(arg);
  365.    diff = va_arg(arg, int);
  366.    format = va_arg(arg, char *);
  367. #endif
  368.    print_msg("Warning", srcline-diff, format, arg);
  369.    va_end(arg);
  370.  
  371.    if (++warnings >= MAX_WARNINGS && MAX_WARNINGS > 0)
  372.       fatal(0,"Too many warnings!");
  373.    }
  374.  
  375.  
  376. #ifndef XFRACT
  377. void notice(char *format, ...)
  378. #else
  379. void notice(va_alist)
  380.     va_dcl
  381. #endif
  382.    {
  383.    va_list arg;
  384. #ifndef XFRACT
  385.    va_start(arg, format);
  386. #else
  387.    char *format;
  388.  
  389.    va_start(arg);
  390.    format = va_arg(arg,char *);
  391. #endif
  392.    print_msg("Note", srcline, format, arg);
  393.    va_end(arg);
  394.    }
  395.  
  396.  
  397. #ifndef XFRACT
  398. void msg(char *format, ...)
  399. #else
  400. void msg(va_alist)
  401. va_dcl
  402. #endif
  403.    {
  404.    va_list arg;
  405. #ifdef XFRACT
  406.    char *format;
  407. #endif
  408.  
  409.    if (quiet_mode)
  410.       return;
  411. #ifndef XFRACT
  412.    va_start(arg, format);
  413. #else
  414.    va_start(arg);
  415.    format = va_arg(arg,char *);
  416. #endif
  417.    print_msg(NULL, 0, format, arg);
  418.    va_end(arg);
  419.    }
  420.  
  421.  
  422. #ifdef SHOW_ERROR_LINE
  423. #   define fatal  (printf("[%04d] ", __LINE__), fatal)
  424. #   define error  (printf("[%04d] ", __LINE__), error)
  425. #   define warn   (printf("[%04d] ", __LINE__), warn)
  426. #   define notice (printf("[%04d] ", __LINE__), notice)
  427. #   define msg      (printf((quiet_mode)?"":"[%04d] ", __LINE__), msg)
  428. #endif
  429.  
  430.  
  431. /*
  432.  * store-topic-text-to-disk stuff.
  433.  */
  434.  
  435.  
  436. void alloc_topic_text(TOPIC *t, unsigned size)
  437.    {
  438.    t->text_len = size;
  439.    t->text = swappos;
  440.    swappos += size;
  441.    fseek(swapfile, t->text, SEEK_SET);
  442.    fwrite(buffer, 1, t->text_len, swapfile);
  443.    }
  444.  
  445.  
  446. char *get_topic_text(TOPIC *t)
  447.    {
  448.    fseek(swapfile, t->text, SEEK_SET);
  449.    fread(buffer, 1, t->text_len, swapfile);
  450.    return (buffer);
  451.    }
  452.  
  453.  
  454. void release_topic_text(TOPIC *t, int save)
  455.    {
  456.    if ( save )
  457.       {
  458.       fseek(swapfile, t->text, SEEK_SET);
  459.       fwrite(buffer, 1, t->text_len, swapfile);
  460.       }
  461.    }
  462.  
  463.  
  464. /*
  465.  * memory-allocation functions.
  466.  */
  467.  
  468.  
  469. #define new(item)    (item *)newx(sizeof(item))
  470. #define delete(item) free(item)
  471.  
  472.  
  473. VOIDPTR newx(unsigned size)
  474.    {
  475.    VOIDPTR ptr;
  476.  
  477.    ptr = malloc(size);
  478.  
  479.    if (ptr == NULL)
  480.       fatal(0,"Out of memory!");
  481.  
  482.    return (ptr);
  483.    }
  484.  
  485.  
  486. VOIDPTR renewx(VOIDPTR ptr, unsigned size)
  487.    {
  488.    ptr = realloc(ptr, size);
  489.  
  490.    if (ptr == NULL)
  491.       fatal(0,"Out of memory!");
  492.  
  493.    return (ptr);
  494.    }
  495.  
  496.  
  497. char *dupstr(char *s, unsigned len)
  498.    {
  499.    char *ptr;
  500.  
  501.    if (len == 0)
  502.       len = strlen(s) + 1;
  503.  
  504.    ptr = newx(len);
  505.  
  506.    memcpy(ptr, s, len);
  507.  
  508.    return (ptr);
  509.    }
  510.  
  511.  
  512. #define LINK_ALLOC_SIZE (16)
  513.  
  514.  
  515. int add_link(LINK *l)
  516.    {
  517.    if (num_link == 0)
  518.       a_link = newx( sizeof(LINK)*LINK_ALLOC_SIZE );
  519.  
  520.    else if (num_link%LINK_ALLOC_SIZE == 0)
  521.       a_link = renewx(a_link, sizeof(LINK) * (num_link+LINK_ALLOC_SIZE) );
  522.  
  523.    a_link[num_link] = *l;
  524.  
  525.    return( num_link++ );
  526.    }
  527.  
  528.  
  529. #define PAGE_ALLOC_SIZE (4)
  530.  
  531.  
  532. int add_page(TOPIC *t, PAGE *p)
  533.    {
  534.    if (t->num_page == 0)
  535.       t->page = newx( sizeof(PAGE)*PAGE_ALLOC_SIZE );
  536.  
  537.    else if (t->num_page%PAGE_ALLOC_SIZE == 0)
  538.       t->page = renewx(t->page, sizeof(PAGE) * (t->num_page+PAGE_ALLOC_SIZE) );
  539.  
  540.    t->page[t->num_page] = *p;
  541.  
  542.    return ( t->num_page++ );
  543.    }
  544.  
  545.  
  546. #define TOPIC_ALLOC_SIZE (16)
  547.  
  548.  
  549. int add_topic(TOPIC *t)
  550.    {
  551.    if (num_topic == 0)
  552.       topic = newx( sizeof(TOPIC)*TOPIC_ALLOC_SIZE );
  553.  
  554.    else if (num_topic%TOPIC_ALLOC_SIZE == 0)
  555.       topic = renewx(topic, sizeof(TOPIC) * (num_topic+TOPIC_ALLOC_SIZE) );
  556.  
  557.    topic[num_topic] = *t;
  558.  
  559.    return ( num_topic++ );
  560.    }
  561.  
  562.  
  563. #define LABEL_ALLOC_SIZE (16)
  564.  
  565.  
  566. int add_label(LABEL *l)
  567.    {
  568.    if (l->name[0] == '@')    /* if it's a private label... */
  569.       {
  570.       if (num_plabel == 0)
  571.      plabel = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  572.  
  573.       else if (num_plabel%LABEL_ALLOC_SIZE == 0)
  574.      plabel = renewx(plabel, sizeof(LABEL) * (num_plabel+LABEL_ALLOC_SIZE) );
  575.  
  576.       plabel[num_plabel] = *l;
  577.  
  578.       return ( num_plabel++ );
  579.       }
  580.    else
  581.       {
  582.       if (num_label == 0)
  583.      label = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  584.  
  585.       else if (num_label%LABEL_ALLOC_SIZE == 0)
  586.      label = renewx(label, sizeof(LABEL) * (num_label+LABEL_ALLOC_SIZE) );
  587.  
  588.       label[num_label] = *l;
  589.  
  590.       return ( num_label++ );
  591.       }
  592.    }
  593.  
  594.  
  595. #define CONTENTS_ALLOC_SIZE (16)
  596.  
  597.  
  598. int add_content(CONTENT *c)
  599.    {
  600.    if (num_contents == 0)
  601.       contents = newx( sizeof(CONTENT)*CONTENTS_ALLOC_SIZE );
  602.  
  603.    else if (num_contents%CONTENTS_ALLOC_SIZE == 0)
  604.       contents = renewx(contents, sizeof(CONTENT) * (num_contents+CONTENTS_ALLOC_SIZE) );
  605.  
  606.    contents[num_contents] = *c;
  607.  
  608.    return ( num_contents++ );
  609.    }
  610.  
  611.  
  612. /*
  613.  * read_char() stuff
  614.  */
  615.  
  616.  
  617. #define READ_CHAR_BUFF_SIZE (32)
  618.  
  619.  
  620. int  read_char_buff[READ_CHAR_BUFF_SIZE];
  621. int  read_char_buff_pos = -1;
  622. int  read_char_sp       = 0;
  623.  
  624.  
  625. void unread_char(int ch)
  626.    /*
  627.     * Will not handle new-lines or tabs correctly!
  628.     */
  629.    {
  630.    if (read_char_buff_pos+1 >= READ_CHAR_BUFF_SIZE)
  631.       fatal(0,"Compiler Error -- Read char buffer overflow!");
  632.  
  633.    read_char_buff[++read_char_buff_pos] = ch;
  634.  
  635.    --srccol;
  636.    }
  637.  
  638.  
  639. void unread_string(char *s)
  640.    {
  641.    int p = strlen(s);
  642.  
  643.    while (p-- > 0)
  644.       unread_char(s[p]);
  645.    }
  646.  
  647.  
  648. int eos(void)     /* end-of-source ? */
  649.    {
  650.    return ( !((read_char_sp==0) && (read_char_buff_pos==0) && feof(srcfile)) );
  651.    }
  652.  
  653.  
  654. int _read_char(void)
  655.    {
  656.    int ch;
  657.  
  658.    if (srcline <= 0)
  659.       {
  660.       srcline = 1;
  661.       srccol = 0;
  662.       }
  663.  
  664.    if (read_char_buff_pos >= 0)
  665.       {
  666.       ++srccol;
  667.       return ( read_char_buff[read_char_buff_pos--] );
  668.       }
  669.  
  670.    if (read_char_sp > 0)
  671.       {
  672.       --read_char_sp;
  673.       return (' ');
  674.       }
  675.  
  676.    if ( feof(srcfile) )
  677.       return (-1);
  678.  
  679.    while (1)
  680.       {
  681.       ch = getc(srcfile);
  682.  
  683.       switch (ch)
  684.      {
  685.      case '\t':    /* expand a tab */
  686.         {
  687.         int diff = ( ( (srccol/8) + 1 ) * 8 ) - srccol;
  688.  
  689.         srccol += diff;
  690.         read_char_sp += diff;
  691.         break;
  692.         }
  693.  
  694.      case ' ':
  695.         ++srccol;
  696.         ++read_char_sp;
  697.         break;
  698.  
  699.      case '\n':
  700.         read_char_sp = 0;   /* delete spaces before a \n */
  701.         srccol = 0;
  702.         ++srcline;
  703.         return ('\n');
  704.  
  705.      case -1:            /* EOF */
  706.         if (read_char_sp > 0)
  707.            {
  708.            --read_char_sp;
  709.            return (' ');
  710.            }
  711.         return (-1);
  712.  
  713.      default:
  714.         if (read_char_sp > 0)
  715.            {
  716.            ungetc(ch, srcfile);
  717.            --read_char_sp;
  718.            return (' ');
  719.            }
  720.  
  721.         ++srccol;
  722.         return (ch);
  723.  
  724.      } /* switch */
  725.       }
  726.    }
  727.  
  728.  
  729. int read_char(void)
  730.    {
  731.    int ch;
  732.  
  733.    ch = _read_char();
  734.  
  735.    while (ch == ';' && srccol==1)    /* skip over comments */
  736.       {
  737.       ch = _read_char();
  738.  
  739.       while (ch!='\n' && ch!=-1 )
  740.      ch = _read_char();
  741.  
  742.       ch = _read_char();
  743.       }
  744.  
  745.    if (ch == '\\')   /* process an escape code */
  746.       {
  747.       ch = _read_char();
  748.  
  749.       if (ch >= '0' && ch <= '9')
  750.      {
  751.      char buff[4];
  752.      int  ctr;
  753.  
  754.      for (ctr=0; ; ctr++)
  755.         {
  756.         if ( ch<'0' || ch>'9' || ch==-1 || ctr>=3 )
  757.            {
  758.            unread_char(ch);
  759.            break;
  760.            }
  761.         buff[ctr] = ch;
  762.         ch = _read_char();
  763.         }
  764.      buff[ctr] = '\0';
  765.      ch = atoi(buff);
  766.      }
  767.  
  768. #ifdef XFRACT
  769.    /* Convert graphics arrows into keyboard chars */
  770.        if (ch>=24 && ch<=27) {
  771.        ch = "KJHL"[ch-24];
  772.        }
  773. #endif
  774.       ch |= 0x100;
  775.       }
  776.  
  777.    if ( (ch & 0xFF) == 0 )
  778.       {
  779.       error(0,"Null character (\'\\0\') not allowed!");
  780.       ch = 0x1FF; /* since we've had an error the file will not be written; */
  781.           /*   the value we return doesn't really matter */
  782.       }
  783.  
  784.    return(ch);
  785.    }
  786.  
  787.  
  788. /*
  789.  * misc. search functions.
  790.  */
  791.  
  792.  
  793. LABEL *find_label(char *name)
  794.    {
  795.    int      l;
  796.    LABEL *lp;
  797.  
  798.    if (*name == '@')
  799.       {
  800.       for (l=0, lp=plabel; l<num_plabel; l++, lp++)
  801.      if ( strcmp(name, lp->name) == 0 )
  802.         return (lp);
  803.       }
  804.    else
  805.       {
  806.       for (l=0, lp=label; l<num_label; l++, lp++)
  807.      if ( strcmp(name, lp->name) == 0 )
  808.         return (lp);
  809.       }
  810.  
  811.    return (NULL);
  812.    }
  813.  
  814.  
  815. int find_topic_title(char *title)
  816.    {
  817.    int t;
  818.    int len;
  819.  
  820.    while (*title == ' ')
  821.       ++title;
  822.  
  823.    len = strlen(title) - 1;
  824.    while ( title[len] == ' ' && len > 0 )
  825.       --len;
  826.  
  827.    ++len;
  828.  
  829.    if ( len > 2 && title[0] == '\"' && title[len-1] == '\"' )
  830.       {
  831.       ++title;
  832.       len -= 2;
  833.       }
  834.  
  835.    for (t=0; t<num_topic; t++)
  836.       if ( strlen(topic[t].title) == len &&
  837.        strnicmp(title, topic[t].title, len) == 0 )
  838.      return (t);
  839.  
  840.    return (-1);   /* not found */
  841.    }
  842.  
  843.  
  844. /*
  845.  * .SRC file parser stuff
  846.  */
  847.  
  848.  
  849. int validate_label_name(char *name)
  850.    {
  851.    if ( !isalpha(*name) && *name!='@' && *name!='_' )
  852.       return (0);  /* invalid */
  853.  
  854.    while (*(++name) != '\0')
  855.       if ( !isalpha(*name) && !isdigit(*name) && *name!='_' )
  856.      return(0);  /* invalid */
  857.  
  858.    return (1);  /* valid */
  859.    }
  860.  
  861.  
  862. char *read_until(char *buff, int len, char *stop_chars)
  863.    {
  864.    int ch;
  865.  
  866.    while ( --len > 0 )
  867.       {
  868.       ch = read_char();
  869.  
  870.       if ( ch == -1 )
  871.      {
  872.      *buff++ = '\0';
  873.      break;
  874.      }
  875.  
  876.       if ( (ch&0xFF) <= MAX_CMD )
  877.      *buff++ = CMD_LITERAL;
  878.  
  879.       *buff++ = ch;
  880.  
  881.       if ( (ch&0x100)==0 && strchr(stop_chars, ch) != NULL )
  882.      break;
  883.       }
  884.  
  885.    return ( buff-1 );
  886.    }
  887.  
  888.  
  889. void skip_over(char *skip)
  890.    {
  891.    int ch;
  892.  
  893.    while (1)
  894.       {
  895.       ch = read_char();
  896.  
  897.       if ( ch == -1 )
  898.      break;
  899.  
  900.       else if ( (ch&0x100) == 0 && strchr(skip, ch) == NULL )
  901.      {
  902.      unread_char(ch);
  903.      break;
  904.      }
  905.       }
  906.    }
  907.  
  908.  
  909. char *pchar(int ch)
  910.    {
  911.    static char buff[16];
  912.  
  913.    if ( ch >= 0x20 && ch <= 0x7E )
  914.       sprintf(buff, "\'%c\'", ch);
  915.    else
  916.       sprintf(buff, "\'\\x%02X\'", ch&0xFF);
  917.  
  918.    return (buff);
  919.    }
  920.  
  921.  
  922. void put_spaces(int how_many)
  923.    {
  924.    if (how_many > 2 && compress_spaces)
  925.       {
  926.       if (how_many > 255)
  927.      {
  928.      error(0,"Too many spaces (over 255).");
  929.      how_many = 255;
  930.      }
  931.  
  932.       *curr++ = CMD_SPACE;
  933.       *curr++ = (BYTE)how_many;
  934.       }
  935.    else
  936.       {
  937.       while (how_many-- > 0)
  938.      *curr++ = ' ';
  939.       }
  940.    }
  941.  
  942.  
  943. int get_next_item(void)   /* used by parse_contents() */
  944.    {
  945.    int     last;
  946.    char *ptr;
  947.  
  948.    skip_over(" \t\n");
  949.    ptr = read_until(cmd, 128, ",}");
  950.    last = (*ptr == '}');
  951.    --ptr;
  952.    while ( ptr >= cmd && strchr(" \t\n",*ptr) )   /* strip trailing spaces */
  953.       --ptr;
  954.    *(++ptr) = '\0';
  955.  
  956.    return (last);
  957.    }
  958.  
  959.  
  960. void process_contents(void)
  961.    {
  962.    CONTENT c;
  963.    char   *ptr;
  964.    int       indent;
  965.    int       ch;
  966.    TOPIC   t;
  967.  
  968.    t.flags     = 0;
  969.    t.title_len = strlen(DOCCONTENTS_TITLE)+1;
  970.    t.title     = dupstr(DOCCONTENTS_TITLE, t.title_len);
  971.    t.doc_page  = -1;
  972.    t.num_page  = 0;
  973.  
  974.    curr = buffer;
  975.  
  976.    c.flags = 0;
  977.    c.id = dupstr("",1);
  978.    c.name = dupstr("",1);
  979.    c.doc_page = -1;
  980.    c.page_num_pos = 0;
  981.    c.num_topic = 1;
  982.    c.is_label[0] = 0;
  983.    c.topic_name[0] = dupstr(DOCCONTENTS_TITLE,0);
  984.    c.srcline = -1;
  985.    add_content(&c);
  986.  
  987.    while (1)
  988.       {
  989.       ch = read_char();
  990.  
  991.       if (ch == '{')   /* process a CONTENT entry */
  992.      {
  993.      int last;
  994.  
  995.      c.flags = 0;
  996.      c.num_topic = 0;
  997.      c.doc_page = -1;
  998.      c.srcfile = src_cfname;
  999.      c.srcline = srcline;
  1000.  
  1001.      if ( get_next_item() )
  1002.         {
  1003.         error(0,"Unexpected end of DocContent entry.");
  1004.         continue;
  1005.         }
  1006.      c.id = dupstr(cmd,0);
  1007.  
  1008.      if ( get_next_item() )
  1009.         {
  1010.         error(0,"Unexpected end of DocContent entry.");
  1011.         continue;
  1012.         }
  1013.      indent = atoi(cmd);
  1014.  
  1015.      last = get_next_item();
  1016.  
  1017.      if ( cmd[0] == '\"' )
  1018.         {
  1019.         ptr = cmd+1;
  1020.         if (ptr[strlen(ptr)-1] == '\"')
  1021.            ptr[strlen(ptr)-1] = '\0';
  1022.         else
  1023.            warn(0,"Missing ending quote.");
  1024.  
  1025.         c.is_label[c.num_topic] = 0;
  1026.         c.topic_name[c.num_topic] = dupstr(ptr,0);
  1027.         ++c.num_topic;
  1028.         c.name = dupstr(ptr,0);
  1029.         }
  1030.      else
  1031.         c.name = dupstr(cmd,0);
  1032.  
  1033.      /* now, make the entry in the buffer */
  1034.  
  1035.      sprintf(curr, "%-5s %*.0s%s", c.id, indent*2, "", c.name);
  1036.      ptr = curr + strlen(curr);
  1037.      while ( (ptr-curr) < PAGE_WIDTH-10 )
  1038.         *ptr++ = '.';
  1039.      c.page_num_pos = (unsigned) ( (ptr-3) - buffer );
  1040.      curr = ptr;
  1041.  
  1042.      while (!last)
  1043.         {
  1044.         last = get_next_item();
  1045.  
  1046.         if ( stricmp(cmd, "FF") == 0 )
  1047.            {
  1048.            if ( c.flags & CF_NEW_PAGE )
  1049.           warn(0,"FF already present in this entry.");
  1050.            c.flags |= CF_NEW_PAGE;
  1051.            continue;
  1052.            }
  1053.  
  1054.         if (cmd[0] == '\"')
  1055.            {
  1056.            ptr = cmd+1;
  1057.            if (ptr[strlen(ptr)-1] == '\"')
  1058.           ptr[strlen(ptr)-1] = '\0';
  1059.            else
  1060.           warn(0,"Missing ending quote.");
  1061.  
  1062.            c.is_label[c.num_topic] = 0;
  1063.            c.topic_name[c.num_topic] = dupstr(ptr,0);
  1064.            }
  1065.         else
  1066.            {
  1067.            c.is_label[c.num_topic] = 1;
  1068.            c.topic_name[c.num_topic] = dupstr(cmd,0);
  1069.            }
  1070.  
  1071.         if ( ++c.num_topic >= MAX_CONTENT_TOPIC )
  1072.            {
  1073.            error(0,"Too many topics in DocContent entry.");
  1074.            break;
  1075.            }
  1076.         }
  1077.  
  1078.      add_content(&c);
  1079.      }
  1080.  
  1081.       else if (ch == '~')   /* end at any command */
  1082.      {
  1083.      unread_char(ch);
  1084.      break;
  1085.      }
  1086.  
  1087.       else
  1088.      *curr++ = ch;
  1089.  
  1090.       CHK_BUFFER(0);
  1091.       }
  1092.  
  1093.    alloc_topic_text(&t, (unsigned) (curr - buffer) );
  1094.    add_topic(&t);
  1095.    }
  1096.  
  1097.  
  1098. int parse_link(void)   /* returns length of link or 0 on error */
  1099.    {
  1100.    char *ptr;
  1101.    char *end;
  1102.    int     bad = 0;
  1103.    int     len;
  1104.    LINK  l;
  1105.    int     lnum;
  1106.    int     err_off;
  1107.  
  1108.    l.srcfile  = src_cfname;
  1109.    l.srcline  = srcline;
  1110.    l.doc_page = -1;
  1111.  
  1112.    end = read_until(cmd, 128, "}\n");   /* get the entire hot-link */
  1113.  
  1114.    if (*end == '\0')
  1115.       {
  1116.       error(0,"Unexpected EOF in hot-link.");
  1117.       return (0);
  1118.       }
  1119.  
  1120.    if (*end == '\n')
  1121.       {
  1122.       err_off = 1;
  1123.       warn(1,"Hot-link has no closing curly-brace (\'}\').");
  1124.       }
  1125.    else
  1126.       err_off = 0;
  1127.  
  1128.    *end = '\0';
  1129.  
  1130.    if (cmd[0] == '=')   /* it's an "explicit" link to a label or "special" */
  1131.       {
  1132.       ptr = strchr(cmd, ' ');
  1133.  
  1134.       if (ptr == NULL)
  1135.      ptr = end;
  1136.       else
  1137.      *ptr++ = '\0';
  1138.  
  1139.       len = (int) (end - ptr);
  1140.  
  1141.       if ( cmd[1] == '-' )
  1142.      {
  1143.      l.type      = 2;       /* type 2 = "special" */
  1144.      l.topic_num = atoi(cmd+1);
  1145.      l.topic_off = 0;
  1146.      l.name      = NULL;
  1147.      }
  1148.       else
  1149.      {
  1150.      l.type = 1;           /* type 1 = to a label */
  1151.      if (strlen(cmd) > 32)
  1152.         warn(err_off, "Label is long.");
  1153.      if (cmd[1] == '\0')
  1154.         {
  1155.         error(err_off, "Explicit hot-link has no Label.");
  1156.         bad = 1;
  1157.         }
  1158.      else
  1159.         l.name = dupstr(cmd+1,0);
  1160.      }
  1161.       if (len == 0)
  1162.      warn(err_off, "Explicit hot-link has no title.");
  1163.       }
  1164.    else
  1165.       {
  1166.       ptr = cmd;
  1167.       l.type = 0;   /* type 0 = topic title */
  1168.       len = (int) (end - ptr);
  1169.       if (len == 0)
  1170.      {
  1171.      error(err_off, "Implicit hot-link has no title.");
  1172.      bad = 1;
  1173.      }
  1174.       l.name = dupstr(ptr,len+1);
  1175.       l.name[len] = '\0';
  1176.       }
  1177.  
  1178.    if ( !bad )
  1179.       {
  1180.       CHK_BUFFER(1+3*sizeof(int)+len+1)
  1181.       lnum = add_link(&l);
  1182.       *curr++ = CMD_LINK;
  1183.       setint(curr,lnum);
  1184.       curr += 3*sizeof(int);
  1185.       memcpy(curr, ptr, len);
  1186.       curr += len;
  1187.       *curr++ = CMD_LINK;
  1188.       return (len);
  1189.       }
  1190.    else
  1191.       return (0);
  1192.    }
  1193.  
  1194.  
  1195. #define MAX_TABLE_SIZE (100)
  1196.  
  1197.  
  1198. int create_table(void)
  1199.    {
  1200.    char  *ptr;
  1201.    int      width;
  1202.    int      cols;
  1203.    int      start_off;
  1204.    int      first_link;
  1205.    int      rows;
  1206.    int      r, c;
  1207.    int      ch;
  1208.    int      done;
  1209.    int      len;
  1210.    int      lnum;
  1211.    int      count;
  1212.    char  *title[MAX_TABLE_SIZE];
  1213.    char  *table_start;
  1214.  
  1215.    ptr = strchr(cmd, '=');
  1216.  
  1217.    if (ptr == NULL)
  1218.       return (0);   /* should never happen! */
  1219.  
  1220.    ptr++;
  1221.  
  1222.    len = sscanf(ptr, " %d %d %d", &width, &cols, &start_off);
  1223.  
  1224.    if (len < 3)
  1225.       {
  1226.       error(1,"Too few arguments to Table.");
  1227.       return (0);
  1228.       }
  1229.  
  1230.    if (width<=0 || width > 78 || cols<=0 || start_off<0 || start_off > 78)
  1231.       {
  1232.       error(1,"Argument out of range.");
  1233.       return (0);
  1234.       }
  1235.  
  1236.    done = 0;
  1237.  
  1238.    first_link = num_link;
  1239.    table_start = curr;
  1240.    count = 0;
  1241.  
  1242.    /* first, read all the links in the table */
  1243.  
  1244.    do
  1245.       {
  1246.  
  1247.       do
  1248.      ch = read_char();
  1249.       while ( ch=='\n' || ch == ' ' );
  1250.  
  1251.       if (done)
  1252.      break;
  1253.  
  1254.       switch (ch)
  1255.      {
  1256.      case -1:
  1257.         error(0,"Unexpected EOF in a Table.");
  1258.         return(0);
  1259.  
  1260.      case '{':
  1261.         if (count >= MAX_TABLE_SIZE)
  1262.            fatal(0,"Table is too large.");
  1263.         len = parse_link();
  1264.         curr = table_start;   /* reset to the start... */
  1265.             title[count] = dupstr(curr+3*sizeof(int)+1, len+1);
  1266.         if (len >= width)
  1267.            {
  1268.            warn(1,"Link is too long; truncating.");
  1269.            len = width-1;
  1270.            }
  1271.         title[count][len] = '\0';
  1272.         ++count;
  1273.         break;
  1274.  
  1275.      case '~':
  1276.         {
  1277.         int imbedded;
  1278.  
  1279.         ch = read_char();
  1280.  
  1281.         if (ch=='(')
  1282.            imbedded = 1;
  1283.         else
  1284.            {
  1285.            imbedded = 0;
  1286.            unread_char(ch);
  1287.            }
  1288.  
  1289.         ptr = read_until(cmd, 128, ")\n,");
  1290.  
  1291.         ch = *ptr;
  1292.         *ptr = '\0';
  1293.  
  1294.         if  ( stricmp(cmd, "EndTable") == 0 )
  1295.            done = 1;
  1296.         else
  1297.            {
  1298.            error(1,"Unexpected command in table \"%s\"", cmd);
  1299.            warn(1,"Command will be ignored.");
  1300.            }
  1301.  
  1302.         if (ch == ',')
  1303.            {
  1304.            if (imbedded)
  1305.           unread_char('(');
  1306.            unread_char('~');
  1307.            }
  1308.         }
  1309.         break;
  1310.  
  1311.      default:
  1312.         error(0,"Unexpected character %s.", pchar(ch));
  1313.         break;
  1314.      }
  1315.       }
  1316.    while (!done);
  1317.  
  1318.    /* now, put all the links into the buffer... */
  1319.  
  1320.    rows = 1 + ( count / cols );
  1321.  
  1322.    for (r=0; r<rows; r++)
  1323.       {
  1324.       put_spaces(start_off);
  1325.       for (c=0; c<cols; c++)
  1326.      {
  1327.      lnum = c*rows + r;
  1328.  
  1329.      if ( first_link+lnum >= num_link )
  1330.         break;
  1331.  
  1332.      len = strlen(title[lnum]);
  1333.      *curr++ = CMD_LINK;
  1334.          setint(curr,first_link+lnum);
  1335.          curr += 3*sizeof(int);
  1336.      memcpy(curr, title[lnum], len);
  1337.      curr += len;
  1338.      *curr++ = CMD_LINK;
  1339.  
  1340.      delete(title[lnum]);
  1341.  
  1342.      if ( c < cols-1 )
  1343.         put_spaces( width-len );
  1344.      }
  1345.       *curr++ = '\n';
  1346.       }
  1347.  
  1348.    return (1);
  1349.    }
  1350.  
  1351.  
  1352. void process_comment(void)
  1353.    {
  1354.    int ch;
  1355.  
  1356.    while ( 1 )
  1357.       {
  1358.       ch = read_char();
  1359.  
  1360.       if (ch == '~')
  1361.      {
  1362.      int   imbedded;
  1363.      char *ptr;
  1364.  
  1365.      ch = read_char();
  1366.  
  1367.      if (ch=='(')
  1368.         imbedded = 1;
  1369.      else
  1370.         {
  1371.         imbedded = 0;
  1372.         unread_char(ch);
  1373.         }
  1374.  
  1375.      ptr = read_until(cmd, 128, ")\n,");
  1376.  
  1377.      ch = *ptr;
  1378.      *ptr = '\0';
  1379.  
  1380.      if  ( stricmp(cmd, "EndComment") == 0 )
  1381.         {
  1382.         if (ch == ',')
  1383.            {
  1384.            if (imbedded)
  1385.           unread_char('(');
  1386.            unread_char('~');
  1387.            }
  1388.         break;
  1389.         }
  1390.      }
  1391.  
  1392.       else if ( ch == -1 )
  1393.      {
  1394.      error(0,"Unexpected EOF in Comment");
  1395.      break;
  1396.      }
  1397.       }
  1398.    }
  1399.  
  1400.  
  1401. void process_bininc(void)
  1402.    {
  1403.    int  handle;
  1404.    long len;
  1405.  
  1406.    if ( (handle=open(cmd+7, O_RDONLY|O_BINARY)) == -1 )
  1407.       {
  1408.       error(0,"Unable to open \"%s\"", cmd+7);
  1409.       return ;
  1410.       }
  1411.  
  1412.    len = filelength(handle);
  1413.  
  1414.    if ( len >= BUFFER_SIZE )
  1415.       {
  1416.       error(0,"File \"%s\" is too large to BinInc (%dK).", cmd+7, (int)(len>>10));
  1417.       close(handle);
  1418.       return ;
  1419.       }
  1420.  
  1421.    /*
  1422.     * Since we know len is less than BUFFER_SIZE (and therefore less then
  1423.     * 64K) we can treat it as an unsigned.
  1424.     */
  1425.  
  1426.    CHK_BUFFER((unsigned)len);
  1427.  
  1428.    read(handle, curr, (unsigned)len);
  1429.  
  1430.    curr += (unsigned)len;
  1431.  
  1432.    close(handle);
  1433.    }
  1434.  
  1435.  
  1436. void start_topic(TOPIC *t, char *title, int title_len)
  1437.    {
  1438.    t->flags = 0;
  1439.    t->title_len = title_len;
  1440.    t->title = dupstr(title, title_len+1);
  1441.    t->title[title_len] = '\0';
  1442.    t->doc_page = -1;
  1443.    t->num_page = 0;
  1444.    curr = buffer;
  1445.    }
  1446.  
  1447.  
  1448. void end_topic(TOPIC *t)
  1449.    {
  1450.    alloc_topic_text(t, (unsigned) (curr - buffer) );
  1451.    add_topic(t);
  1452.    }
  1453.  
  1454.  
  1455. int end_of_sentence(char *ptr)  /* true if ptr is at the end of a sentence */
  1456.    {
  1457.    if ( *ptr == ')')
  1458.       --ptr;
  1459.  
  1460.    if ( *ptr == '\"')
  1461.       --ptr;
  1462.  
  1463.    return ( *ptr=='.' || *ptr=='?' || *ptr=='!' );
  1464.    }
  1465.  
  1466.  
  1467. void add_blank_for_split(void)     /* add space at curr for merging two lines */
  1468.    {
  1469.    if ( !is_hyphen(curr-1) )   /* no spaces if it's a hyphen */
  1470.       {
  1471.       if ( end_of_sentence(curr-1) )
  1472.      *curr++ = ' ';  /* two spaces at end of a sentence */
  1473.       *curr++ = ' ';
  1474.       }
  1475.    }
  1476.  
  1477.  
  1478. void put_a_char(int ch, TOPIC *t)
  1479.    {
  1480.    if (ch == '{' && !(t->flags & TF_DATA) )   /* is it a hot-link? */
  1481.       parse_link();
  1482.    else
  1483.       {
  1484.       if ( (ch&0xFF) <= MAX_CMD)
  1485.      *curr++ = CMD_LITERAL;
  1486.       *curr++ = ch;
  1487.       }
  1488.    }
  1489.  
  1490.  
  1491. enum STATES   /* states for FSM's */
  1492.    {
  1493.    S_Start,            /* initial state, between paragraphs       */
  1494.    S_StartFirstLine,        /* spaces at start of first line           */
  1495.    S_FirstLine,         /* text on the first line               */
  1496.    S_FirstLineSpaces,        /* spaces on the first line            */
  1497.    S_StartSecondLine,        /* spaces at start of second line           */
  1498.    S_Line,            /* text on lines after the first           */
  1499.    S_LineSpaces,        /* spaces on lines after the first           */
  1500.    S_StartLine,         /* spaces at start of lines after second       */
  1501.    S_FormatDisabled,        /* format automatically disabled for this line */
  1502.    S_FormatDisabledSpaces,  /* spaces in line which format is disabled       */
  1503.    S_Spaces
  1504.    } ;
  1505.  
  1506.  
  1507. void check_command_length(int eoff, int len)
  1508.    {
  1509.    if (strlen(cmd) != len)
  1510.       error(eoff, "Invalid text after a command \"%s\"", cmd+len);
  1511.    }
  1512.  
  1513.  
  1514. void read_src(char *fname)
  1515.    {
  1516.    int      ch;
  1517.    char  *ptr;
  1518.    TOPIC  t;
  1519.    LABEL  lbl;
  1520.    char  *margin_pos = NULL;
  1521.    int      in_topic   = 0,
  1522.       formatting = 1,
  1523.       state      = S_Start,
  1524.       num_spaces = 0,
  1525.       margin     = 0,
  1526.       in_para    = 0,
  1527.       centering  = 0,
  1528.       lformat_exclude = format_exclude,
  1529.       again;
  1530.  
  1531.    xonline = xdoc = 0;
  1532.  
  1533.    src_cfname = fname;
  1534.  
  1535.    if ( (srcfile = fopen(fname, "rt")) == NULL )
  1536.       fatal(0,"Unable to open \"%s\"", fname);
  1537.  
  1538.    msg("Compiling: %s", fname);
  1539.  
  1540.    in_topic = 0;
  1541.  
  1542.    curr = buffer;
  1543.  
  1544.    while ( 1 )
  1545.       {
  1546.  
  1547.       ch = read_char();
  1548.  
  1549.       if ( ch == -1 )   /* EOF? */
  1550.      {
  1551.      if ( include_stack_top >= 0)
  1552.         {
  1553.         fclose(srcfile);
  1554.         src_cfname = include_stack[include_stack_top].fname;
  1555.         srcfile = include_stack[include_stack_top].file;
  1556.         srcline = include_stack[include_stack_top].line;
  1557.         srccol  = include_stack[include_stack_top].col;
  1558.         --include_stack_top;
  1559.         continue;
  1560.         }
  1561.      else
  1562.         {
  1563.         if (in_topic)  /* if we're in a topic, finish it */
  1564.            end_topic(&t);
  1565.         if (num_topic == 0)
  1566.            warn(0,".SRC file has no topics.");
  1567.         break;
  1568.         }
  1569.      }
  1570.  
  1571.       if (ch == '~')   /* is is a command? */
  1572.      {
  1573.      int imbedded;
  1574.      int eoff;
  1575.      int done;
  1576.  
  1577.      ch = read_char();
  1578.      if (ch == '(')
  1579.         {
  1580.         imbedded = 1;
  1581.         eoff = 0;
  1582.         }
  1583.      else
  1584.         {
  1585.         imbedded = 0;
  1586.         eoff=0;
  1587.         unread_char(ch);
  1588.         }
  1589.  
  1590.      done = 0;
  1591.  
  1592.      while ( !done )
  1593.         {
  1594.         do
  1595.            ch = read_char();
  1596.         while (ch == ' ');
  1597.         unread_char(ch);
  1598.  
  1599.         if (imbedded)
  1600.            ptr = read_until(cmd, 128, ")\n,");
  1601.         else
  1602.            ptr = read_until(cmd, 128, "\n,");
  1603.  
  1604.         done = 1;
  1605.  
  1606.         if ( *ptr == '\0' )
  1607.            {
  1608.            error(0,"Unexpected EOF in command.");
  1609.            break;
  1610.            }
  1611.  
  1612.         if (*ptr == '\n')
  1613.            ++eoff;
  1614.  
  1615.         if ( imbedded && *ptr == '\n' )
  1616.            error(eoff,"Imbedded command has no closing parend (\')\')");
  1617.  
  1618.         done = (*ptr != ',');   /* we done if it's not a comma */
  1619.  
  1620.         if ( *ptr != '\n' && *ptr != ')' && *ptr != ',' )
  1621.            {
  1622.            error(0,"Command line too long.");
  1623.            break;
  1624.            }
  1625.  
  1626.         *ptr = '\0';
  1627.  
  1628.  
  1629.         /* commands allowed anytime... */
  1630.  
  1631.         if ( strnicmp(cmd, "Topic=", 6) == 0 )
  1632.            {
  1633.            if (in_topic)  /* if we're in a topic, finish it */
  1634.           end_topic(&t);
  1635.            else
  1636.           in_topic = 1;
  1637.  
  1638.            if (cmd[6] == '\0')
  1639.           warn(eoff,"Topic has no title.");
  1640.  
  1641.            else if (strlen(cmd+6) > 70)
  1642.           error(eoff,"Topic title is too long.");
  1643.  
  1644.            else if (strlen(cmd+6) > 60)
  1645.           warn(eoff,"Topic title is long.");
  1646.  
  1647.            if ( find_topic_title(cmd+6) != -1 )
  1648.           error(eoff,"Topic title already exists.");
  1649.  
  1650.            start_topic(&t, cmd+6, (unsigned)(ptr-(cmd+6)));
  1651.            formatting = 1;
  1652.            centering = 0;
  1653.            state = S_Start;
  1654.            in_para = 0;
  1655.            num_spaces = 0;
  1656.            xonline = xdoc = 0;
  1657.            lformat_exclude = format_exclude;
  1658.            compress_spaces = 1;
  1659.            continue;
  1660.            }
  1661.  
  1662.         else if ( strnicmp(cmd, "Data=", 5) == 0 )
  1663.            {
  1664.            if (in_topic)  /* if we're in a topic, finish it */
  1665.           end_topic(&t);
  1666.            else
  1667.           in_topic = 1;
  1668.  
  1669.            if (cmd[5] == '\0')
  1670.           warn(eoff,"Data topic has no label.");
  1671.  
  1672.            if ( !validate_label_name(cmd+5) )
  1673.           {
  1674.           error(eoff,"Label \"%s\" contains illegal characters.", cmd+5);
  1675.           continue;
  1676.           }
  1677.  
  1678.            if ( find_label(cmd+5) != NULL )
  1679.           {
  1680.           error(eoff,"Label \"%s\" already exists", cmd+5);
  1681.           continue;
  1682.           }
  1683.  
  1684.            if ( cmd[5] == '@' )
  1685.           warn(eoff, "Data topic has a local label.");
  1686.  
  1687.            start_topic(&t, "", 0);
  1688.            t.flags |= TF_DATA;
  1689.  
  1690.            if (strlen(cmd+5) > 32)
  1691.           warn(eoff,"Label name is long.");
  1692.  
  1693.            lbl.name      = dupstr(cmd+5, 0);
  1694.            lbl.topic_num = num_topic;
  1695.            lbl.topic_off = 0;
  1696.            lbl.doc_page  = -1;
  1697.            add_label(&lbl);
  1698.  
  1699.            formatting = 0;
  1700.            centering = 0;
  1701.            state = S_Start;
  1702.            in_para = 0;
  1703.            num_spaces = 0;
  1704.            xonline = xdoc = 0;
  1705.            lformat_exclude = format_exclude;
  1706.            compress_spaces = 0;
  1707.            continue;
  1708.            }
  1709.  
  1710.         else if ( strnicmp(cmd, "DocContents", 11) == 0 )
  1711.            {
  1712.            check_command_length(eoff, 11);
  1713.            if (in_topic)  /* if we're in a topic, finish it */
  1714.           end_topic(&t);
  1715.            if (!done)
  1716.           {
  1717.           if (imbedded)
  1718.              unread_char('(');
  1719.           unread_char('~');
  1720.           done = 1;
  1721.           }
  1722.            compress_spaces = 1;
  1723.            process_contents();
  1724.            in_topic = 0;
  1725.            continue;
  1726.            }
  1727.  
  1728.         else if ( stricmp(cmd, "Comment") == 0 )
  1729.            {
  1730.            process_comment();
  1731.            continue;
  1732.            }
  1733.  
  1734.         else if ( strnicmp(cmd, "FormatExclude", 13) == 0 )
  1735.            {
  1736.            if (cmd[13] == '-')
  1737.           {
  1738.           check_command_length(eoff, 14);
  1739.           if ( in_topic )
  1740.              {
  1741.              if (lformat_exclude > 0)
  1742.                 lformat_exclude = -lformat_exclude;
  1743.              else
  1744.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1745.              }
  1746.           else
  1747.              {
  1748.              if (format_exclude > 0)
  1749.                 format_exclude = -format_exclude;
  1750.              else
  1751.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1752.              }
  1753.           }
  1754.            else if (cmd[13] == '+')
  1755.           {
  1756.           check_command_length(eoff,14);
  1757.           if ( in_topic )
  1758.              {
  1759.              if (lformat_exclude < 0)
  1760.                 lformat_exclude = -lformat_exclude;
  1761.              else
  1762.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1763.              }
  1764.           else
  1765.              {
  1766.              if (format_exclude < 0)
  1767.                 format_exclude = -format_exclude;
  1768.              else
  1769.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1770.              }
  1771.           }
  1772.            else if (cmd[13] == '=')
  1773.           {
  1774.           if (cmd[14] == 'n' || cmd[14] == 'N')
  1775.              {
  1776.              check_command_length(eoff,15);
  1777.              if (in_topic)
  1778.                 lformat_exclude = 0;
  1779.              else
  1780.                format_exclude = 0;
  1781.              }
  1782.           else if (cmd[14] == '\0')
  1783.              lformat_exclude = format_exclude;
  1784.           else
  1785.              {
  1786.              int n = ( ( (in_topic) ? lformat_exclude : format_exclude) < 0 ) ? -1 : 1;
  1787.  
  1788.              lformat_exclude = atoi(cmd+14);
  1789.  
  1790.              if ( lformat_exclude <= 0 )
  1791.                 {
  1792.                 error(eoff,"Invalid argument to FormatExclude=");
  1793.                 lformat_exclude = 0;
  1794.                 }
  1795.  
  1796.              lformat_exclude *= n;
  1797.  
  1798.              if ( !in_topic )
  1799.                 format_exclude = lformat_exclude;
  1800.              }
  1801.           }
  1802.            else
  1803.           error(eoff,"Invalid format for FormatExclude");
  1804.  
  1805.            continue;
  1806.            }
  1807.  
  1808.         else if ( strnicmp(cmd, "Include ", 8) == 0 )
  1809.            {
  1810.            if (include_stack_top >= MAX_INCLUDE_STACK-1)
  1811.           error(eoff, "Too many nested Includes.");
  1812.            else
  1813.           {
  1814.           ++include_stack_top;
  1815.           include_stack[include_stack_top].fname = src_cfname;
  1816.           include_stack[include_stack_top].file = srcfile;
  1817.           include_stack[include_stack_top].line = srcline;
  1818.           include_stack[include_stack_top].col  = srccol;
  1819.           strupr(cmd+8);
  1820.           if ( (srcfile = fopen(cmd+8, "rt")) == NULL )
  1821.              {
  1822.              error(eoff, "Unable to open \"%s\"", cmd+8);
  1823.              srcfile = include_stack[include_stack_top--].file;
  1824.              }
  1825.           src_cfname = dupstr(cmd+8,0);  /* never deallocate! */
  1826.           srcline = 1;
  1827.           srccol = 0;
  1828.           }
  1829.  
  1830.            continue;
  1831.            }
  1832.  
  1833.  
  1834.         /* commands allowed only before all topics... */
  1835.  
  1836.         if ( !in_topic )
  1837.            {
  1838.            if ( strnicmp(cmd, "HdrFile=", 8) == 0 )
  1839.           {
  1840.           if (hdr_fname[0] != '\0')
  1841.              warn(eoff,"Header Filename has already been defined.");
  1842.           strcpy(hdr_fname, cmd+8);
  1843.           strupr(hdr_fname);
  1844.           }
  1845.  
  1846.            else if ( strnicmp(cmd, "HlpFile=", 8) == 0 )
  1847.           {
  1848.           if (hlp_fname[0] != '\0')
  1849.              warn(eoff,"Help Filename has already been defined.");
  1850.           strcpy(hlp_fname, cmd+8);
  1851.           strupr(hlp_fname);
  1852.           }
  1853.  
  1854.            else if ( strnicmp(cmd, "Version=", 8) == 0 )
  1855.           {
  1856.           if (version != -1)   /* an unlikely value */
  1857.              warn(eoff,"Help version has already been defined");
  1858.           version = atoi(cmd+8);
  1859.           }
  1860.  
  1861.            else
  1862.           error(eoff,"Bad or unexpected command \"%s\"", cmd);
  1863.  
  1864.            continue;
  1865.            }
  1866.  
  1867.  
  1868.         /* commands allowed only in a topic... */
  1869.  
  1870.         else
  1871.            {
  1872.            if (strnicmp(cmd, "FF", 2) == 0 )
  1873.           {
  1874.           check_command_length(eoff,2);
  1875.           if ( in_para )
  1876.              *curr++ = '\n';  /* finish off current paragraph */
  1877.           *curr++ = CMD_FF;
  1878.           state = S_Start;
  1879.           in_para = 0;
  1880.           num_spaces = 0;
  1881.           }
  1882.  
  1883.            else if (strnicmp(cmd, "DocFF", 5) == 0 )
  1884.           {
  1885.           check_command_length(eoff,5);
  1886.           if ( in_para )
  1887.              *curr++ = '\n';  /* finish off current paragraph */
  1888.           if (!xonline)
  1889.              *curr++ = CMD_XONLINE;
  1890.           *curr++ = CMD_FF;
  1891.           if (!xonline)
  1892.              *curr++ = CMD_XONLINE;
  1893.           state = S_Start;
  1894.           in_para = 0;
  1895.           num_spaces = 0;
  1896.           }
  1897.  
  1898.            else if (strnicmp(cmd, "OnlineFF", 8) == 0 )
  1899.           {
  1900.           check_command_length(eoff,8);
  1901.           if ( in_para )
  1902.              *curr++ = '\n';  /* finish off current paragraph */
  1903.           if (!xdoc)
  1904.              *curr++ = CMD_XDOC;
  1905.           *curr++ = CMD_FF;
  1906.           if (!xdoc)
  1907.              *curr++ = CMD_XDOC;
  1908.           state = S_Start;
  1909.           in_para = 0;
  1910.           num_spaces = 0;
  1911.           }
  1912.  
  1913.            else if ( strnicmp(cmd, "Label=", 6) == 0 )
  1914.           {
  1915.           if (strlen(cmd+6) <= 0)
  1916.              error(eoff,"Label has no name.");
  1917.  
  1918.           else if ( !validate_label_name(cmd+6) )
  1919.              error(eoff,"Label \"%s\" contains illegal characters.", cmd+6);
  1920.  
  1921.           else if ( find_label(cmd+6) != NULL )
  1922.              error(eoff,"Label \"%s\" already exists", cmd+6);
  1923.  
  1924.           else
  1925.              {
  1926.              if (strlen(cmd+6) > 32)
  1927.                 warn(eoff,"Label name is long.");
  1928.  
  1929.             if ( (t.flags & TF_DATA) && cmd[6] == '@' )
  1930.                warn(eoff, "Data topic has a local label.");
  1931.  
  1932.              lbl.name       = dupstr(cmd+6, 0);
  1933.              lbl.topic_num = num_topic;
  1934.              lbl.topic_off = (unsigned)(curr - buffer);
  1935.              lbl.doc_page  = -1;
  1936.              add_label(&lbl);
  1937.              }
  1938.           }
  1939.  
  1940.            else if ( strnicmp(cmd, "Table=", 6) == 0 )
  1941.           {
  1942.           if ( in_para )
  1943.              {
  1944.              *curr++ = '\n';  /* finish off current paragraph */
  1945.              in_para = 0;
  1946.              num_spaces = 0;
  1947.              state = S_Start;
  1948.              }
  1949.  
  1950.           if (!done)
  1951.              {
  1952.              if (imbedded)
  1953.                 unread_char('(');
  1954.              unread_char('~');
  1955.              done = 1;
  1956.              }
  1957.  
  1958.           create_table();
  1959.           }
  1960.  
  1961.            else if ( strnicmp(cmd, "FormatExclude", 12) == 0 )
  1962.           {
  1963.           if (cmd[13] == '-')
  1964.              {
  1965.              check_command_length(eoff,14);
  1966.              if (lformat_exclude > 0)
  1967.                 lformat_exclude = -lformat_exclude;
  1968.              else
  1969.                 warn(0,"\"FormatExclude-\" is already in effect.");
  1970.              }
  1971.           else if (cmd[13] == '+')
  1972.              {
  1973.              check_command_length(eoff,14);
  1974.              if (lformat_exclude < 0)
  1975.                 lformat_exclude = -lformat_exclude;
  1976.              else
  1977.                 warn(0,"\"FormatExclude+\" is already in effect.");
  1978.              }
  1979.           else
  1980.              error(eoff,"Unexpected or invalid argument to FormatExclude.");
  1981.           }
  1982.  
  1983.            else if ( strnicmp(cmd, "Format", 6) == 0 )
  1984.           {
  1985.           if (cmd[6] == '+')
  1986.              {
  1987.              check_command_length(eoff,7);
  1988.              if ( !formatting )
  1989.                 {
  1990.                 formatting = 1;
  1991.                 in_para = 0;
  1992.                 num_spaces = 0;
  1993.                 state = S_Start;
  1994.                 }
  1995.              else
  1996.                 warn(eoff,"\"Format+\" is already in effect.");
  1997.              }
  1998.           else if (cmd[6] == '-')
  1999.              {
  2000.              check_command_length(eoff,7);
  2001.              if ( formatting )
  2002.                 {
  2003.                 if ( in_para )
  2004.                *curr++ = '\n';  /* finish off current paragraph */
  2005.                 state = S_Start;
  2006.                 in_para = 0;
  2007.                 formatting = 0;
  2008.                 num_spaces = 0;
  2009.                 state = S_Start;
  2010.                 }
  2011.              else
  2012.                 warn(eoff,"\"Format-\" is already in effect.");
  2013.              }
  2014.           else
  2015.              error(eoff,"Invalid argument to Format.");
  2016.           }
  2017.  
  2018.            else if ( strnicmp(cmd, "Online", 6) == 0 )
  2019.           {
  2020.           if (cmd[6] == '+')
  2021.              {
  2022.              check_command_length(eoff,7);
  2023.  
  2024.              if ( xonline )
  2025.                 {
  2026.                 *curr++ = CMD_XONLINE;
  2027.                 xonline = 0;
  2028.                 }
  2029.              else
  2030.                 warn(eoff,"\"Online+\" already in effect.");
  2031.              }
  2032.           else if (cmd[6] == '-')
  2033.              {
  2034.              check_command_length(eoff,7);
  2035.              if ( !xonline )
  2036.                 {
  2037.                 *curr++ = CMD_XONLINE;
  2038.                 xonline = 1;
  2039.                 }
  2040.              else
  2041.                 warn(eoff,"\"Online-\" already in effect.");
  2042.              }
  2043.           else
  2044.              error(eoff,"Invalid argument to Online.");
  2045.           }
  2046.  
  2047.            else if ( strnicmp(cmd, "Doc", 3) == 0 )
  2048.           {
  2049.           if (cmd[3] == '+')
  2050.              {
  2051.              check_command_length(eoff,4);
  2052.              if ( xdoc )
  2053.                 {
  2054.                 *curr++ = CMD_XDOC;
  2055.                 xdoc = 0;
  2056.                 }
  2057.              else
  2058.                 warn(eoff,"\"Doc+\" already in effect.");
  2059.              }
  2060.           else if (cmd[3] == '-')
  2061.              {
  2062.              check_command_length(eoff,4);
  2063.              if ( !xdoc )
  2064.                 {
  2065.                 *curr++ = CMD_XDOC;
  2066.                 xdoc = 1;
  2067.                 }
  2068.              else
  2069.                 warn(eoff,"\"Doc-\" already in effect.");
  2070.              }
  2071.           else
  2072.              error(eoff,"Invalid argument to Doc.");
  2073.           }
  2074.  
  2075.            else if ( strnicmp(cmd, "Center", 6) == 0 )
  2076.           {
  2077.           if (cmd[6] == '+')
  2078.              {
  2079.              check_command_length(eoff,7);
  2080.              if ( !centering )
  2081.                 {
  2082.                 centering = 1;
  2083.                 if ( in_para )
  2084.                {
  2085.                *curr++ = '\n';
  2086.                in_para = 0;
  2087.                }
  2088.                 state = S_Start;  /* for centering FSM */
  2089.                 }
  2090.              else
  2091.                 warn(eoff,"\"Center+\" already in effect.");
  2092.              }
  2093.           else if (cmd[6] == '-')
  2094.              {
  2095.              check_command_length(eoff,7);
  2096.              if ( centering )
  2097.                 {
  2098.                 centering = 0;
  2099.                 state = S_Start;  /* for centering FSM */
  2100.                 }
  2101.              else
  2102.                 warn(eoff,"\"Center-\" already in effect.");
  2103.              }
  2104.           else
  2105.              error(eoff,"Invalid argument to Center.");
  2106.           }
  2107.  
  2108.            else if ( strnicmp(cmd, "CompressSpaces", 14) == 0 )
  2109.           {
  2110.           check_command_length(eoff,15);
  2111.  
  2112.           if ( cmd[14] == '+' )
  2113.              {
  2114.              if ( compress_spaces )
  2115.                 warn(eoff,"\"CompressSpaces+\" is already in effect.");
  2116.              else
  2117.                 compress_spaces = 1;
  2118.              }
  2119.           else if ( cmd[14] == '-' )
  2120.              {
  2121.              if ( !compress_spaces )
  2122.                 warn(eoff,"\"CompressSpaces-\" is already in effect.");
  2123.              else
  2124.                 compress_spaces = 0;
  2125.              }
  2126.           else
  2127.              error(eoff,"Invalid argument to CompressSpaces.");
  2128.           }
  2129.  
  2130.            else if ( strnicmp("BinInc ", cmd, 7) == 0 )
  2131.           {
  2132.           if ( !(t.flags & TF_DATA) )
  2133.              error(eoff,"BinInc allowed only in Data topics.");
  2134.           else
  2135.              process_bininc();
  2136.           }
  2137.  
  2138.            else
  2139.           error(eoff,"Bad or unexpected command \"%s\".", cmd);
  2140.            } /* else */
  2141.  
  2142.         } /* while (!done) */
  2143.  
  2144.      continue;
  2145.      }
  2146.  
  2147.       if ( !in_topic )
  2148.      {
  2149.      cmd[0] = ch;
  2150.      ptr = read_until(cmd+1, 127, "\n~");
  2151.      if (*ptr == '~')
  2152.         unread_char('~');
  2153.      *ptr = '\0';
  2154.      error(0,"Text outside of any topic \"%s\".", cmd);
  2155.      continue;
  2156.      }
  2157.  
  2158.       if ( centering )
  2159.      {
  2160.      do
  2161.         {
  2162.         again = 0;     /* default */
  2163.  
  2164.         switch (state)
  2165.            {
  2166.            case S_Start:
  2167.           if (ch == ' ')
  2168.              ; /* do nothing */
  2169.           else if ( (ch&0xFF) == '\n' )
  2170.              *curr++ = ch;  /* no need to center blank lines. */
  2171.           else
  2172.              {
  2173.              *curr++ = CMD_CENTER;
  2174.              state = S_Line;
  2175.              again = 1;
  2176.              }
  2177.           break;
  2178.  
  2179.            case S_Line:
  2180.           put_a_char(ch, &t);
  2181.           if ( (ch&0xFF) == '\n')
  2182.              state = S_Start;
  2183.           break;
  2184.            } /* switch */
  2185.         }
  2186.      while (again);
  2187.      }
  2188.  
  2189.       else if ( formatting )
  2190.      {
  2191.      int again;
  2192.  
  2193.      do
  2194.         {
  2195.         again = 0;     /* default */
  2196.  
  2197.         switch (state)
  2198.            {
  2199.            case S_Start:
  2200.           if ( (ch&0xFF) == '\n' )
  2201.              *curr++ = ch;
  2202.           else
  2203.              {
  2204.              state = S_StartFirstLine;
  2205.              num_spaces = 0;
  2206.              again = 1;
  2207.              }
  2208.           break;
  2209.  
  2210.            case S_StartFirstLine:
  2211.           if ( ch == ' ')
  2212.              ++num_spaces;
  2213.  
  2214.           else
  2215.              {
  2216.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2217.                 {
  2218.                 put_spaces(num_spaces);
  2219.                 num_spaces = 0;
  2220.                 state = S_FormatDisabled;
  2221.                 again = 1;
  2222.                 }
  2223.              else
  2224.                 {
  2225.                 *curr++ = CMD_PARA;
  2226.                 *curr++ = (char)num_spaces;
  2227.                 *curr++ = (char)num_spaces;
  2228.                 margin_pos = curr - 1;
  2229.                 state = S_FirstLine;
  2230.                 again = 1;
  2231.                 in_para = 1;
  2232.                 }
  2233.              }
  2234.           break;
  2235.  
  2236.            case S_FirstLine:
  2237.           if (ch == '\n')
  2238.              {
  2239.              state = S_StartSecondLine;
  2240.              num_spaces = 0;
  2241.              }
  2242.           else if (ch == ('\n'|0x100) )   /* force end of para ? */
  2243.              {
  2244.              *curr++ = '\n';
  2245.              in_para = 0;
  2246.              state = S_Start;
  2247.              }
  2248.           else if ( ch == ' ' )
  2249.              {
  2250.              state = S_FirstLineSpaces;
  2251.              num_spaces = 1;
  2252.              }
  2253.           else
  2254.              put_a_char(ch, &t);
  2255.           break;
  2256.  
  2257.            case S_FirstLineSpaces:
  2258.           if (ch == ' ')
  2259.              ++num_spaces;
  2260.           else
  2261.              {
  2262.              put_spaces(num_spaces);
  2263.              state = S_FirstLine;
  2264.              again = 1;
  2265.              }
  2266.           break;
  2267.  
  2268.            case S_StartSecondLine:
  2269.           if ( ch == ' ')
  2270.              ++num_spaces;
  2271.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2272.              {
  2273.              *curr++ = '\n';   /* end the para */
  2274.              *curr++ = '\n';   /* for the blank line */
  2275.              in_para = 0;
  2276.              state = S_Start;
  2277.              }
  2278.           else
  2279.              {
  2280.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2281.                 {
  2282.                 *curr++ = '\n';
  2283.                 in_para = 0;
  2284.                 put_spaces(num_spaces);
  2285.                 num_spaces = 0;
  2286.                 state = S_FormatDisabled;
  2287.                 again = 1;
  2288.                 }
  2289.              else
  2290.                 {
  2291.                 add_blank_for_split();
  2292.                 margin = num_spaces;
  2293.                 *margin_pos = (char)num_spaces;
  2294.                 state = S_Line;
  2295.                 again = 1;
  2296.                 }
  2297.              }
  2298.           break;
  2299.  
  2300.            case S_Line:   /* all lines after the first */
  2301.           if (ch == '\n')
  2302.              {
  2303.              state = S_StartLine;
  2304.              num_spaces = 0;
  2305.              }
  2306.           else if (ch == ('\n' | 0x100) )   /* force end of para ? */
  2307.              {
  2308.              *curr++ = '\n';
  2309.              in_para = 0;
  2310.              state = S_Start;
  2311.              }
  2312.           else if ( ch == ' ' )
  2313.              {
  2314.              state = S_LineSpaces;
  2315.              num_spaces = 1;
  2316.              }
  2317.           else
  2318.              put_a_char(ch, &t);
  2319.           break;
  2320.  
  2321.            case S_LineSpaces:
  2322.           if (ch == ' ')
  2323.              ++num_spaces;
  2324.           else
  2325.              {
  2326.              put_spaces(num_spaces);
  2327.              state = S_Line;
  2328.              again = 1;
  2329.              }
  2330.           break;
  2331.  
  2332.            case S_StartLine:   /* for all lines after the second */
  2333.           if ( ch == ' ')
  2334.              ++num_spaces;
  2335.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2336.              {
  2337.              *curr++ = '\n';   /* end the para */
  2338.              *curr++ = '\n';   /* for the blank line */
  2339.              in_para = 0;
  2340.              state = S_Start;
  2341.              }
  2342.           else
  2343.              {
  2344.              if ( num_spaces != margin )
  2345.                 {
  2346.                 *curr++ = '\n';
  2347.                 in_para = 0;
  2348.                 state = S_StartFirstLine;  /* with current num_spaces */
  2349.                 again = 1;
  2350.                 }
  2351.              else
  2352.                 {
  2353.                 add_blank_for_split();
  2354.                 state = S_Line;
  2355.                 again = 1;
  2356.                 }
  2357.              }
  2358.           break;
  2359.  
  2360.            case S_FormatDisabled:
  2361.           if ( ch == ' ' )
  2362.              {
  2363.              state = S_FormatDisabledSpaces;
  2364.              num_spaces = 1;
  2365.              }
  2366.           else
  2367.              {
  2368.              if ( (ch&0xFF) == '\n' )
  2369.                 state = S_Start;
  2370.              put_a_char(ch, &t);
  2371.              }
  2372.           break;
  2373.  
  2374.            case S_FormatDisabledSpaces:
  2375.           if ( ch == ' ' )
  2376.              ++num_spaces;
  2377.           else
  2378.              {
  2379.              put_spaces(num_spaces);
  2380.              num_spaces = 0;    /* is this needed? */
  2381.              state = S_FormatDisabled;
  2382.              again = 1;
  2383.              }
  2384.           break;
  2385.  
  2386.            } /* switch (state) */
  2387.         }
  2388.      while (again);
  2389.      }
  2390.  
  2391.       else
  2392.      {
  2393.      do
  2394.         {
  2395.         again = 0;     /* default */
  2396.  
  2397.         switch (state)
  2398.            {
  2399.            case S_Start:
  2400.           if ( ch == ' ' )
  2401.              {
  2402.              state = S_Spaces;
  2403.              num_spaces = 1;
  2404.              }
  2405.           else
  2406.              put_a_char(ch, &t);
  2407.           break;
  2408.  
  2409.            case S_Spaces:
  2410.           if (ch == ' ')
  2411.              ++num_spaces;
  2412.           else
  2413.              {
  2414.              put_spaces(num_spaces);
  2415.              num_spaces = 0;     /* is this needed? */
  2416.              state = S_Start;
  2417.              again = 1;
  2418.              }
  2419.           break;
  2420.            } /* switch */
  2421.         }
  2422.      while (again);
  2423.      }
  2424.  
  2425.       CHK_BUFFER(0)
  2426.       } /* while ( 1 ) */
  2427.  
  2428.    fclose(srcfile);
  2429.  
  2430.    srcline = -1;
  2431.    }
  2432.  
  2433.  
  2434. /*
  2435.  * stuff to resolve hot-link references.
  2436.  */
  2437.  
  2438.  
  2439. void make_hot_links(void)
  2440.    /*
  2441.     * calculate topic_num/topic_off for each link.
  2442.     */
  2443.    {
  2444.    LINK    *l;
  2445.    LABEL   *lbl;
  2446.    int        lctr;
  2447.    int        t;
  2448.    CONTENT *c;
  2449.    int        ctr;
  2450.  
  2451.    msg("Making hot-links.");
  2452.  
  2453.    /*
  2454.     * Calculate topic_num for all entries in DocContents.  Also set
  2455.     * "TF_IN_DOC" flag for all topics included in the document.
  2456.     */
  2457.  
  2458.    for (lctr=0, c=contents; lctr<num_contents; lctr++, c++)
  2459.       {
  2460.       for (ctr=0; ctr<c->num_topic; ctr++)
  2461.      {
  2462.      if ( c->is_label[ctr] )
  2463.         {
  2464.         lbl = find_label(c->topic_name[ctr]);
  2465.         if (lbl == NULL)
  2466.            {
  2467.            src_cfname = c->srcfile;
  2468.            srcline = c->srcline;
  2469.            error(0,"Cannot find DocContent label \"%s\".", c->topic_name[ctr]);
  2470.            srcline = -1;
  2471.            }
  2472.         else
  2473.            {
  2474.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2475.           {
  2476.           src_cfname = c->srcfile;
  2477.           srcline = c->srcline;
  2478.           error(0,"Label \"%s\" is a data-only topic.", c->topic_name[ctr]);
  2479.           srcline = -1;
  2480.           }
  2481.            else
  2482.           {
  2483.           c->topic_num[ctr] = lbl->topic_num;
  2484.           if ( topic[lbl->topic_num].flags & TF_IN_DOC )
  2485.              warn(0,"Topic \"%s\" appears in document more than once.",
  2486.               topic[lbl->topic_num].title);
  2487.           else
  2488.              topic[lbl->topic_num].flags |= TF_IN_DOC;
  2489.           }
  2490.            }
  2491.  
  2492.         }
  2493.      else
  2494.         {
  2495.         t = find_topic_title(c->topic_name[ctr]);
  2496.  
  2497.         if (t == -1)
  2498.            {
  2499.            src_cfname = c->srcfile;
  2500.            srcline = c->srcline;
  2501.            error(0,"Cannot find DocContent topic \"%s\".", c->topic_name[ctr]);
  2502.            srcline = -1;  /* back to reality */
  2503.            }
  2504.         else
  2505.            {
  2506.            c->topic_num[ctr] = t;
  2507.            if ( topic[t].flags & TF_IN_DOC )
  2508.           warn(0,"Topic \"%s\" appears in document more than once.",
  2509.                topic[t].title);
  2510.            else
  2511.           topic[t].flags |= TF_IN_DOC;
  2512.            }
  2513.         }
  2514.      }
  2515.       }
  2516.  
  2517.    /*
  2518.     * Find topic_num and topic_off for all hot-links.  Also flag all hot-
  2519.     * links which will (probably) appear in the document.
  2520.     */
  2521.  
  2522.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2523.       {
  2524.       switch ( l->type )
  2525.      {
  2526.      case 0:      /* name is the title of the topic */
  2527.         t = find_topic_title(l->name);
  2528.         if (t == -1)
  2529.            {
  2530.            src_cfname = l->srcfile;
  2531.            srcline = l->srcline; /* pretend we are still in the source... */
  2532.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2533.            srcline = -1;  /* back to reality */
  2534.            }
  2535.         else
  2536.            {
  2537.            l->topic_num = t;
  2538.            l->topic_off = 0;
  2539.            l->doc_page = (topic[t].flags & TF_IN_DOC) ? 0 : -1;
  2540.            }
  2541.         break;
  2542.  
  2543.      case 1:  /* name is the name of a label */
  2544.         lbl = find_label(l->name);
  2545.         if (lbl == NULL)
  2546.            {
  2547.            src_cfname = l->srcfile;
  2548.            srcline = l->srcline; /* pretend again */
  2549.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2550.            srcline = -1;
  2551.            }
  2552.         else
  2553.            {
  2554.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2555.           {
  2556.           src_cfname = l->srcfile;
  2557.           srcline = l->srcline;
  2558.           error(0,"Label \"%s\" is a data-only topic.", l->name);
  2559.           srcline = -1;
  2560.           }
  2561.            else
  2562.           {
  2563.           l->topic_num = lbl->topic_num;
  2564.           l->topic_off = lbl->topic_off;
  2565.           l->doc_page  = (topic[lbl->topic_num].flags & TF_IN_DOC) ? 0 : -1;
  2566.           }
  2567.            }
  2568.         break;
  2569.  
  2570.      case 2:   /* it's a "special" link; topic_off already has the value */
  2571.         break;
  2572.      }
  2573.       }
  2574.  
  2575.    }
  2576.  
  2577.  
  2578. /*
  2579.  * online help pagination stuff
  2580.  */
  2581.  
  2582.  
  2583. void add_page_break(TOPIC *t, int margin, char *text, char *start, char *curr, int num_links)
  2584.    {
  2585.    PAGE p;
  2586.  
  2587.    p.offset = (unsigned) (start - text);
  2588.    p.length = (unsigned) (curr - start);
  2589.    p.margin = margin;
  2590.    add_page(t, &p);
  2591.  
  2592.    if (max_links < num_links)
  2593.       max_links = num_links;
  2594.    }
  2595.  
  2596.  
  2597. void paginate_online(void)    /* paginate the text for on-line help */
  2598.    {               /* also calculates max_pages and max_links */
  2599.    int         lnum;
  2600.    char     *start;
  2601.    char     *curr;
  2602.    char     *text;
  2603.    TOPIC    *t;
  2604.    int         tctr;
  2605.    unsigned  len;
  2606.    int         skip_blanks;
  2607.    int         num_links;
  2608.    int         col;
  2609.    int         tok;
  2610.    int         size,
  2611.          width;
  2612.    int         start_margin;
  2613.  
  2614.    msg("Paginating online help.");
  2615.  
  2616.    for (t=topic, tctr=0; tctr<num_topic; t++, tctr++)
  2617.       {
  2618.       if ( t->flags & TF_DATA )
  2619.      continue;    /* don't paginate data topics */
  2620.  
  2621.       text = get_topic_text(t);
  2622.       curr = text;
  2623.       len  = t->text_len;
  2624.  
  2625.       start = curr;
  2626.       skip_blanks = 0;
  2627.       lnum = 0;
  2628.       num_links = 0;
  2629.       col = 0;
  2630.       start_margin = -1;
  2631.  
  2632.       while (len > 0)
  2633.      {
  2634.      tok = find_token_length(ONLINE, curr, len, &size, &width);
  2635.  
  2636.      switch ( tok )
  2637.         {
  2638.         case TOK_PARA:
  2639.            {
  2640.            int indent,
  2641.            margin;
  2642.  
  2643.            ++curr;
  2644.  
  2645.            indent = *curr++;
  2646.            margin = *curr++;
  2647.  
  2648.            len -= 3;
  2649.  
  2650.            col = indent;
  2651.  
  2652.            while (1)
  2653.           {
  2654.           tok = find_token_length(ONLINE, curr, len, &size, &width);
  2655.  
  2656.           if (tok == TOK_DONE || tok == TOK_NL || tok == TOK_FF )
  2657.              break;
  2658.  
  2659.           if ( tok == TOK_PARA )
  2660.              {
  2661.              col = 0;   /* fake a nl */
  2662.              ++lnum;
  2663.              break;
  2664.              }
  2665.  
  2666.           if (tok == TOK_XONLINE || tok == TOK_XDOC )
  2667.              {
  2668.              curr += size;
  2669.              len -= size;
  2670.              continue;
  2671.              }
  2672.  
  2673.           /* now tok is TOK_SPACE or TOK_LINK or TOK_WORD */
  2674.  
  2675.           if (col+width > SCREEN_WIDTH)
  2676.              {            /* go to next line... */
  2677.              if ( ++lnum >= SCREEN_DEPTH )
  2678.                 {        /* go to next page... */
  2679.                 add_page_break(t, start_margin, text, start, curr, num_links);
  2680.                 start = curr + ( (tok == TOK_SPACE) ? size : 0 );
  2681.                 start_margin = margin;
  2682.                 lnum = 0;
  2683.                 num_links = 0;
  2684.                 }
  2685.              if ( tok == TOK_SPACE )
  2686.                 width = 0;   /* skip spaces at start of a line */
  2687.  
  2688.              col = margin;
  2689.              }
  2690.  
  2691.           col += width;
  2692.           curr += size;
  2693.           len -= size;
  2694.           }
  2695.  
  2696.            skip_blanks = 0;
  2697.            width = size = 0;
  2698.            break;
  2699.            }
  2700.  
  2701.         case TOK_NL:
  2702.            if (skip_blanks && col == 0)
  2703.           {
  2704.           start += size;
  2705.           break;
  2706.           }
  2707.            ++lnum;
  2708.            if ( lnum >= SCREEN_DEPTH || (col == 0 && lnum==SCREEN_DEPTH-1) )
  2709.           {
  2710.           add_page_break(t, start_margin, text, start, curr, num_links);
  2711.           start = curr + size;
  2712.           start_margin = -1;
  2713.           lnum = 0;
  2714.           num_links = 0;
  2715.           skip_blanks = 1;
  2716.           }
  2717.            col = 0;
  2718.            break;
  2719.  
  2720.         case TOK_FF:
  2721.            col = 0;
  2722.            if (skip_blanks)
  2723.           {
  2724.           start += size;
  2725.           break;
  2726.           }
  2727.            add_page_break(t, start_margin, text, start, curr, num_links);
  2728.            start_margin = -1;
  2729.            start = curr + size;
  2730.            lnum = 0;
  2731.            num_links = 0;
  2732.            break;
  2733.  
  2734.         case TOK_DONE:
  2735.         case TOK_XONLINE:   /* skip */
  2736.         case TOK_XDOC:      /* ignore */
  2737.         case TOK_CENTER:    /* ignore */
  2738.            break;
  2739.  
  2740.         case TOK_LINK:
  2741.            ++num_links;
  2742.  
  2743.            /* fall-through */
  2744.  
  2745.         default:    /* TOK_SPACE, TOK_LINK, TOK_WORD */
  2746.            skip_blanks = 0;
  2747.            break;
  2748.  
  2749.         } /* switch */
  2750.  
  2751.      curr += size;
  2752.      len  -= size;
  2753.      col  += width;
  2754.      } /* while */
  2755.  
  2756.       if (!skip_blanks)
  2757.      add_page_break(t, start_margin, text, start, curr, num_links);
  2758.  
  2759.       if (max_pages < t->num_page)
  2760.      max_pages = t->num_page;
  2761.  
  2762.       release_topic_text(t, 0);
  2763.       } /* for */
  2764.    }
  2765.  
  2766.  
  2767. /*
  2768.  * paginate document stuff
  2769.  */
  2770.  
  2771.  
  2772. #define CNUM           0
  2773. #define TNUM           1
  2774. #define LINK_DEST_WARN 2
  2775.  
  2776.  
  2777. typedef struct
  2778.    {
  2779.    int        cnum,  /* must match above #defines so pd_get_info() will work */
  2780.         tnum,
  2781.         link_dest_warn;
  2782.  
  2783.    char far *start;
  2784.    CONTENT  *c;
  2785.    LABEL    *lbl;
  2786.  
  2787.    } PAGINATE_DOC_INFO;
  2788.  
  2789.  
  2790. LABEL *find_next_label_by_topic(int t)
  2791.    {
  2792.    LABEL *temp, *g, *p;
  2793.    int      ctr;
  2794.  
  2795.    g = p = NULL;
  2796.  
  2797.    for (temp=label, ctr=0; ctr<num_label; ctr++, temp++)
  2798.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2799.      {
  2800.      g = temp;
  2801.      break;
  2802.      }
  2803.       else if (temp->topic_num > t)
  2804.      break;
  2805.  
  2806.    for (temp=plabel, ctr=0; ctr<num_plabel; ctr++, temp++)
  2807.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2808.      {
  2809.      p = temp;
  2810.      break;
  2811.      }
  2812.       else if (temp->topic_num > t)
  2813.      break;
  2814.  
  2815.    if ( p == NULL )
  2816.       return (g);
  2817.  
  2818.    else if ( g == NULL )
  2819.       return (p);
  2820.  
  2821.    else
  2822.       return ( (g->topic_off < p->topic_off) ? g : p );
  2823.    }
  2824.  
  2825.  
  2826. void set_hot_link_doc_page(void)
  2827.    /*
  2828.     * Find doc_page for all hot-links.
  2829.     */
  2830.    {
  2831.    LINK  *l;
  2832.    LABEL *lbl;
  2833.    int      lctr;
  2834.    int      t;
  2835.  
  2836.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2837.       {
  2838.       switch ( l->type )
  2839.      {
  2840.      case 0:      /* name is the title of the topic */
  2841.         t = find_topic_title(l->name);
  2842.         if (t == -1)
  2843.            {
  2844.            src_cfname = l->srcfile;
  2845.            srcline = l->srcline; /* pretend we are still in the source... */
  2846.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2847.            srcline = -1;  /* back to reality */
  2848.            }
  2849.         else
  2850.            l->doc_page = topic[t].doc_page;
  2851.         break;
  2852.  
  2853.      case 1:  /* name is the name of a label */
  2854.         lbl = find_label(l->name);
  2855.         if (lbl == NULL)
  2856.            {
  2857.            src_cfname = l->srcfile;
  2858.            srcline = l->srcline; /* pretend again */
  2859.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2860.            srcline = -1;
  2861.            }
  2862.         else
  2863.            l->doc_page = lbl->doc_page;
  2864.         break;
  2865.  
  2866.      case 2:   /* special topics don't appear in the document */
  2867.         break;
  2868.      }
  2869.       }
  2870.    }
  2871.  
  2872.  
  2873. void set_content_doc_page(void)
  2874.    /*
  2875.     * insert page #'s in the DocContents
  2876.     */
  2877.    {
  2878.    CONTENT *c;
  2879.    TOPIC   *t;
  2880.    char    *base;
  2881.    int        tnum;
  2882.    int        ctr;
  2883.    char     buf[4];
  2884.    int        len;
  2885.  
  2886.    tnum = find_topic_title(DOCCONTENTS_TITLE);
  2887.    assert(tnum>=0);
  2888.    t = &topic[tnum];
  2889.  
  2890.    base = get_topic_text(t);
  2891.  
  2892.    for (ctr=1, c=contents+1; ctr<num_contents; ctr++, c++)
  2893.       {
  2894.       assert(c->doc_page>=1);
  2895.       sprintf(buf, "%d", c->doc_page);
  2896.       len = strlen(buf);
  2897.       assert(len<=3);
  2898.       memcpy(base+c->page_num_pos+(3-len), buf, len);
  2899.       }
  2900.  
  2901.    release_topic_text(t, 1);
  2902.    }
  2903.  
  2904.  
  2905. int pd_get_info(int cmd, PD_INFO *pd, int *info)
  2906.    {         /* this funtion also used by print_document() */
  2907.    CONTENT *c;
  2908.  
  2909.    switch (cmd)
  2910.       {
  2911.       case PD_GET_CONTENT:
  2912.      if ( ++info[CNUM] >= num_contents )
  2913.         return (0);
  2914.      c = &contents[info[CNUM]];
  2915.      info[TNUM] = -1;
  2916.      pd->id       = c->id;
  2917.      pd->title    = c->name;
  2918.      pd->new_page = (c->flags & CF_NEW_PAGE) ? 1 : 0;
  2919.      return (1);
  2920.  
  2921.       case PD_GET_TOPIC:
  2922.      c = &contents[info[CNUM]];
  2923.      if ( ++info[TNUM] >= c->num_topic )
  2924.         return (0);
  2925.      pd->curr = get_topic_text( &topic[c->topic_num[info[TNUM]]] );
  2926.      pd->len = topic[c->topic_num[info[TNUM]]].text_len;
  2927.      return (1);
  2928.  
  2929.       case PD_GET_LINK_PAGE:
  2930.          if ( a_link[getint(pd->s)].doc_page == -1 )
  2931.         {
  2932.         if ( info[LINK_DEST_WARN] )
  2933.            {
  2934.                src_cfname = a_link[getint(pd->s)].srcfile;
  2935.                srcline    = a_link[getint(pd->s)].srcline;
  2936.            warn(0,"Hot-link destination is not in the document.");
  2937.            srcline = -1;
  2938.            }
  2939.         return (0);
  2940.         }
  2941.          pd->i = a_link[getint(pd->s)].doc_page;
  2942.      return (1);
  2943.  
  2944.       case PD_RELEASE_TOPIC:
  2945.      c = &contents[info[CNUM]];
  2946.      release_topic_text(&topic[c->topic_num[info[TNUM]]], 0);
  2947.      return (1);
  2948.  
  2949.       default:
  2950.      return (0);
  2951.       }
  2952.    }
  2953.  
  2954.  
  2955. int paginate_doc_output(int cmd, PD_INFO *pd, PAGINATE_DOC_INFO *info)
  2956.    {
  2957.    switch (cmd)
  2958.       {
  2959.       case PD_FOOTING:
  2960.       case PD_PRINT:
  2961.       case PD_PRINTN:
  2962.       case PD_PRINT_SEC:
  2963.      return (1);
  2964.  
  2965.       case PD_HEADING:
  2966.      ++num_doc_pages;
  2967.      return (1);
  2968.  
  2969.       case PD_START_SECTION:
  2970.      info->c = &contents[info->cnum];
  2971.      return (1);
  2972.  
  2973.       case PD_START_TOPIC:
  2974.      info->start = pd->curr;
  2975.      info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2976.      return (1);
  2977.  
  2978.       case PD_SET_SECTION_PAGE:
  2979.      info->c->doc_page = pd->pnum;
  2980.      return (1);
  2981.  
  2982.       case PD_SET_TOPIC_PAGE:
  2983.      topic[info->c->topic_num[info->tnum]].doc_page = pd->pnum;
  2984.      return (1);
  2985.  
  2986.       case PD_PERIODIC:
  2987.      while ( info->lbl != NULL && (unsigned)(pd->curr - info->start) >= info->lbl->topic_off)
  2988.         {
  2989.         info->lbl->doc_page = pd->pnum;
  2990.         info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2991.         }
  2992.      return (1);
  2993.  
  2994.       default:
  2995.      return (0);
  2996.       }
  2997.    }
  2998.  
  2999.  
  3000. void paginate_document(void)
  3001.    {
  3002.    PAGINATE_DOC_INFO info;
  3003.  
  3004.    if (num_contents == 0)
  3005.       return ;
  3006.  
  3007.    msg("Paginating document.");
  3008.  
  3009.    info.cnum = info.tnum = -1;
  3010.    info.link_dest_warn = 1;
  3011.  
  3012.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)paginate_doc_output, &info);
  3013.  
  3014.    set_hot_link_doc_page();
  3015.    set_content_doc_page();
  3016.    }
  3017.  
  3018.  
  3019. /*
  3020.  * label sorting stuff
  3021.  */
  3022.  
  3023.  
  3024. int fcmp_LABEL(VOIDCONSTPTR a, VOIDCONSTPTR b)
  3025.    {
  3026.    char *an = ((LABEL *)a)->name,
  3027.         *bn = ((LABEL *)b)->name;
  3028.    int     diff;
  3029.  
  3030.    /* compare the names, making sure that the index goes first */
  3031.  
  3032.    if ( (diff=strcmp(an,bn)) == 0 )
  3033.       return (0);
  3034.  
  3035.    if ( strcmp(an, INDEX_LABEL) == 0 )
  3036.       return (-1);
  3037.  
  3038.    if ( strcmp(bn, INDEX_LABEL) == 0 )
  3039.       return (1);
  3040.  
  3041.    return ( diff );
  3042.    }
  3043.  
  3044.  
  3045. void sort_labels(void)
  3046.    {
  3047.    qsort(label,  num_label,  sizeof(LABEL), fcmp_LABEL);
  3048.    qsort(plabel, num_plabel, sizeof(LABEL), fcmp_LABEL);
  3049.    }
  3050.  
  3051.  
  3052. /*
  3053.  * file write stuff.
  3054.  */
  3055.  
  3056.  
  3057. int compare_files(FILE *f1, FILE *f2) /* returns TRUE if different */
  3058.    {
  3059.    if ( filelength(fileno(f1)) != filelength(fileno(f2)) )
  3060.       return (1);   /* different if sizes are not the same */
  3061.  
  3062.    while ( !feof(f1) && !feof(f2) )
  3063.       if ( getc(f1) != getc(f2) )
  3064.      return (1);
  3065.  
  3066.    return ( ( feof(f1) && feof(f2) ) ? 0 : 1);
  3067.    }
  3068.  
  3069.  
  3070. void _write_hdr(char *fname, FILE *file)
  3071.    {
  3072.    int ctr;
  3073.    char nfile[MAXFILE],
  3074.         next[MAXEXT];
  3075.  
  3076.    FNSPLIT(fname, NULL, NULL, nfile, next);
  3077.    fprintf(file, "\n/*\n * %s%s\n", nfile, next);
  3078.    FNSPLIT(src_fname, NULL, NULL, nfile, next);
  3079.    fprintf(file, " *\n * Contains #defines for help.\n *\n");
  3080.    fprintf(file, " * Generated by HC from: %s%s\n *\n */\n\n\n", nfile, next);
  3081.  
  3082.    fprintf(file, "/* current help file version */\n");
  3083.    fprintf(file, "\n");
  3084.    fprintf(file, "#define %-32s %3d\n", "HELP_VERSION", version);
  3085.    fprintf(file, "\n\n");
  3086.  
  3087.    fprintf(file, "/* labels */\n\n");
  3088.  
  3089.    for (ctr=0; ctr<num_label; ctr++)
  3090.       if (label[ctr].name[0] != '@')  /* if it's not a local label... */
  3091.      {
  3092.      fprintf(file, "#define %-32s %3d", label[ctr].name, ctr);
  3093.      if ( strcmp(label[ctr].name, INDEX_LABEL) == 0 )
  3094.         fprintf(file, "        /* index */");
  3095.      fprintf(file, "\n");
  3096.      }
  3097.  
  3098.    fprintf(file, "\n\n");
  3099.    }
  3100.  
  3101.  
  3102. void write_hdr(char *fname)
  3103.    {
  3104.    FILE *temp,
  3105.         *hdr;
  3106.  
  3107.    hdr = fopen(fname, "rt");
  3108.  
  3109.    if (hdr == NULL)
  3110.       {         /* if no prev. hdr file generate a new one */
  3111.       hdr = fopen(fname, "wt");
  3112.       if (hdr == NULL)
  3113.      fatal(0,"Cannot create \"%s\".", fname);
  3114.       msg("Writing: %s", fname);
  3115.       _write_hdr(fname, hdr);
  3116.       fclose(hdr);
  3117.       notice("FRACTINT must be re-compiled.");
  3118.       return ;
  3119.       }
  3120.  
  3121.    msg("Comparing: %s", fname);
  3122.  
  3123.    temp = fopen(TEMP_FNAME, "wt");
  3124.  
  3125.    if (temp == NULL)
  3126.       fatal(0,"Cannot create temporary file: \"%s\".", TEMP_FNAME);
  3127.  
  3128.    _write_hdr(fname, temp);
  3129.  
  3130.    fclose(temp);
  3131.    temp = fopen(TEMP_FNAME, "rt");
  3132.  
  3133.    if (temp == NULL)
  3134.       fatal(0,"Cannot open temporary file: \"%s\".", TEMP_FNAME);
  3135.  
  3136.    if ( compare_files(temp, hdr) )   /* if they are different... */
  3137.       {
  3138.       msg("Updating: %s", fname);
  3139.       fclose(temp);
  3140.       fclose(hdr);
  3141.       unlink(fname);           /* delete the old hdr file */
  3142.       rename(TEMP_FNAME, fname);   /* rename the temp to the hdr file */
  3143.       notice("FRACTINT must be re-compiled.");
  3144.       }
  3145.    else
  3146.       {   /* if they are the same leave the original alone. */
  3147.       fclose(temp);
  3148.       fclose(hdr);
  3149.       unlink(TEMP_FNAME);      /* delete the temp */
  3150.       }
  3151.    }
  3152.  
  3153.  
  3154. void calc_offsets(void)    /* calc file offset to each topic */
  3155.    {
  3156.    int        t;
  3157.    TOPIC   *tp;
  3158.    long     offset;
  3159.    CONTENT *cp;
  3160.    int        c;
  3161.  
  3162.    /* NOTE: offsets do NOT include 6 bytes for signature & version! */
  3163.  
  3164.    offset = sizeof(int) +           /* max_pages */
  3165.             sizeof(int) +           /* max_links */
  3166.             sizeof(int) +           /* num_topic */
  3167.             sizeof(int) +           /* num_label */
  3168.             sizeof(int) +           /* num_contents */
  3169.             sizeof(int) +           /* num_doc_pages */
  3170.             num_topic*sizeof(long) +/* offsets to each topic */
  3171.             num_label*2*sizeof(int);/* topic_num/topic_off for all public labels */
  3172.  
  3173.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3174.       offset += sizeof(int) +       /* flags */
  3175.             1 +            /* id length */
  3176.             strlen(cp->id) +    /* id text */
  3177.             1 +            /* name length */
  3178.             strlen(cp->name) +  /* name text */
  3179.             1 +            /* number of topics */
  3180.                 cp->num_topic*sizeof(int);    /* topic numbers */
  3181.  
  3182.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3183.       {
  3184.       tp->offset = offset;
  3185.       offset += (long)sizeof(int) + /* topic flags */
  3186.                 sizeof(int) +       /* number of pages */
  3187.                 tp->num_page*3*sizeof(int) +   /* page offset, length & starting margin */
  3188.             1 +            /* length of title */
  3189.             tp->title_len +     /* title */
  3190.                 sizeof(int) +       /* length of text */
  3191.             tp->text_len;        /* text */
  3192.       }
  3193.  
  3194.    }
  3195.  
  3196.  
  3197. void insert_real_link_info(char *curr, unsigned len)
  3198.    /*
  3199.     * Replaces link indexes in the help text with topic_num, topic_off and
  3200.     * doc_page info.
  3201.     */
  3202.    {
  3203.    int         size;
  3204.    int         tok;
  3205.    LINK     *l;
  3206.  
  3207.    while (len > 0)
  3208.       {
  3209.       tok = find_token_length(0, curr, len, &size, NULL);
  3210.  
  3211.       if ( tok == TOK_LINK )
  3212.      {
  3213.          l = &a_link[ getint(curr+1) ];
  3214.          setint(curr+1,l->topic_num);
  3215.          setint(curr+1+sizeof(int),l->topic_off);
  3216.          setint(curr+1+2*sizeof(int),l->doc_page);
  3217.      }
  3218.  
  3219.       len -= size;
  3220.       curr += size;
  3221.       }
  3222.    }
  3223.  
  3224.  
  3225. void _write_help(FILE *file)
  3226.    {
  3227.    int             t, p, l, c;
  3228.    char             *text;
  3229.    TOPIC            *tp;
  3230.    CONTENT            *cp;
  3231.    struct help_sig_info  hs;
  3232.  
  3233.    /* write the signature and version */
  3234.  
  3235.    hs.sig = HELP_SIG; /* Edit line 17 of helpcom.h if this is a syntax error */
  3236.    hs.version = version;
  3237.  
  3238.    fwrite(&hs, sizeof(long)+sizeof(int), 1, file);
  3239.  
  3240.    /* write max_pages & max_links */
  3241.  
  3242.    putw(max_pages, file);
  3243.    putw(max_links, file);
  3244.  
  3245.    /* write num_topic, num_label and num_contents */
  3246.  
  3247.    putw(num_topic, file);
  3248.    putw(num_label, file);
  3249.    putw(num_contents, file);
  3250.  
  3251.    /* write num_doc_page */
  3252.  
  3253.    putw(num_doc_pages, file);
  3254.  
  3255.    /* write the offsets to each topic */
  3256.  
  3257.    for (t=0; t<num_topic; t++) 
  3258.       fwrite(&topic[t].offset, sizeof(long), 1, file);
  3259.  
  3260.    /* write all public labels */
  3261.  
  3262.    for (l=0; l<num_label; l++)
  3263.       {
  3264.       putw(label[l].topic_num, file);
  3265.       putw(label[l].topic_off, file);
  3266.       }
  3267.  
  3268.    /* write contents */
  3269.  
  3270.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3271.       {
  3272.       putw(cp->flags, file);
  3273.  
  3274.       t = strlen(cp->id);
  3275.       putc((BYTE)t, file);
  3276.       fwrite(cp->id, 1, t, file);
  3277.  
  3278.       t = strlen(cp->name);
  3279.       putc((BYTE)t, file);
  3280.       fwrite(cp->name, 1, t, file);
  3281.  
  3282.       putc((BYTE)cp->num_topic, file);
  3283.       fwrite(cp->topic_num, sizeof(int), cp->num_topic, file);
  3284.       }
  3285.  
  3286.    /* write topics */
  3287.  
  3288.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3289.       {
  3290.       /* write the topics flags */
  3291.  
  3292.       putw(tp->flags, file);
  3293.  
  3294.       /* write offset, length and starting margin for each page */
  3295.  
  3296.       putw(tp->num_page, file);
  3297.       for (p=0; p<tp->num_page; p++)
  3298.      {
  3299.      putw(tp->page[p].offset, file);
  3300.      putw(tp->page[p].length, file);
  3301.      putw(tp->page[p].margin, file);
  3302.      }
  3303.  
  3304.       /* write the help title */
  3305.  
  3306.       putc((BYTE)tp->title_len, file);
  3307.       fwrite(tp->title, 1, tp->title_len, file);
  3308.  
  3309.       /* insert hot-link info & write the help text */
  3310.  
  3311.       text = get_topic_text(tp);
  3312.  
  3313.       if ( !(tp->flags & TF_DATA) )   /* don't process data topics... */
  3314.      insert_real_link_info(text, tp->text_len);
  3315.  
  3316.       putw(tp->text_len, file);
  3317.       fwrite(text, 1, tp->text_len, file);
  3318.  
  3319.       release_topic_text(tp, 0);  /* don't save the text even though        */
  3320.                   /* insert_real_link_info() modified it    */
  3321.                   /* because we don't access the info after */
  3322.                   /* this.                    */
  3323.  
  3324.       }
  3325.    }
  3326.  
  3327.  
  3328. void write_help(char *fname)
  3329.    {
  3330.    FILE *hlp;
  3331.  
  3332.    hlp = fopen(fname, "wb");
  3333.  
  3334.    if (hlp == NULL)
  3335.       fatal(0,"Cannot create .HLP file: \"%s\".", fname);
  3336.  
  3337.    msg("Writing: %s", fname);
  3338.  
  3339.    _write_help(hlp);
  3340.  
  3341.    fclose(hlp);
  3342.    }
  3343.  
  3344.  
  3345. /*
  3346.  * print document stuff.
  3347.  */
  3348.  
  3349.  
  3350. typedef struct
  3351.    {
  3352.  
  3353.    /*
  3354.     * Note: Don't move these first three or pd_get_info will work not
  3355.     *        correctly.
  3356.     */
  3357.  
  3358.    int        cnum;
  3359.    int        tnum;
  3360.    int        link_dest_warn;   /* = 0 */
  3361.  
  3362.    FILE    *file;
  3363.    int        margin;
  3364.    int        start_of_line;
  3365.    int        spaces;
  3366.    } PRINT_DOC_INFO;
  3367.  
  3368.  
  3369. void printerc(PRINT_DOC_INFO *info, int c, int n)
  3370.    {
  3371.    while ( n-- > 0 )
  3372.       {
  3373.       if (c==' ')
  3374.      ++info->spaces;
  3375.  
  3376.       else if (c=='\n' || c=='\f')
  3377.      {
  3378.      info->start_of_line = 1;
  3379.      info->spaces = 0;   /* strip spaces before a new-line */
  3380.      putc(c, info->file);
  3381.      }
  3382.  
  3383.       else
  3384.      {
  3385.      if (info->start_of_line)
  3386.         {
  3387.         info->spaces += info->margin;
  3388.         info->start_of_line = 0;
  3389.         }
  3390.  
  3391.      while (info->spaces > 0)
  3392.         {
  3393.         fputc(' ', info->file);
  3394.         --info->spaces;
  3395.         }
  3396.  
  3397.      fputc(c, info->file);
  3398.      }
  3399.       }
  3400.    }
  3401.  
  3402.  
  3403. void printers(PRINT_DOC_INFO *info, char far *s, int n)
  3404.    {
  3405.    if (n > 0)
  3406.       {
  3407.       while ( n-- > 0 )
  3408.      printerc(info, *s++, 1);
  3409.       }
  3410.    else
  3411.       {
  3412.       while ( *s != '\0' )
  3413.      printerc(info, *s++, 1);
  3414.       }
  3415.    }
  3416.  
  3417.  
  3418. int print_doc_output(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
  3419.    {
  3420.    switch (cmd)
  3421.       {
  3422.       case PD_HEADING:
  3423.      {
  3424.      char buff[20];
  3425.  
  3426.      info->margin = 0;
  3427.      printers(info, "\n                     Fractint Version xx.xx                     Page ", 0);
  3428.      sprintf(buff, "%d\n\n", pd->pnum);
  3429.      printers(info, buff, 0);
  3430.      info->margin = PAGE_INDENT;
  3431.      return (1);
  3432.      }
  3433.  
  3434.       case PD_FOOTING:
  3435.      info->margin = 0;
  3436.      printerc(info, '\f', 1);
  3437.      info->margin = PAGE_INDENT;
  3438.      return (1);
  3439.  
  3440.       case PD_PRINT:
  3441.      printers(info, pd->s, pd->i);
  3442.      return (1);
  3443.  
  3444.       case PD_PRINTN:
  3445.      printerc(info, *pd->s, pd->i);
  3446.      return (1);
  3447.  
  3448.       case PD_PRINT_SEC:
  3449.      info->margin = TITLE_INDENT;
  3450.      if (pd->id[0] != '\0')
  3451.         {
  3452.         printers(info, pd->id, 0);
  3453.         printerc(info, ' ', 1);
  3454.         }
  3455.      printers(info, pd->title, 0);
  3456.      printerc(info, '\n', 1);
  3457.      info->margin = PAGE_INDENT;
  3458.      return (1);
  3459.  
  3460.       case PD_START_SECTION:
  3461.       case PD_START_TOPIC:
  3462.       case PD_SET_SECTION_PAGE:
  3463.       case PD_SET_TOPIC_PAGE:
  3464.       case PD_PERIODIC:
  3465.      return (1);
  3466.  
  3467.       default:
  3468.      return (0);
  3469.       }
  3470.    }
  3471.  
  3472.  
  3473. void print_document(char *fname)
  3474.    {
  3475.    PRINT_DOC_INFO info;
  3476.  
  3477.    if (num_contents == 0)
  3478.       fatal(0,".SRC has no DocContents.");
  3479.  
  3480.    msg("Printing to: %s", fname);
  3481.  
  3482.    info.cnum = info.tnum = -1;
  3483.    info.link_dest_warn = 0;
  3484.  
  3485.    if ( (info.file = fopen(fname, "wt")) == NULL )
  3486.       fatal(0,"Couldn't create \"%s\"", fname);
  3487.  
  3488.    info.margin = PAGE_INDENT;
  3489.    info.start_of_line = 1;
  3490.    info.spaces = 0;
  3491.  
  3492.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)print_doc_output, &info);
  3493.  
  3494.    fclose(info.file);
  3495.    }
  3496.  
  3497.  
  3498. /*
  3499.  * compiler status and memory usage report stuff.
  3500.  */
  3501.  
  3502.  
  3503. void report_memory(void)
  3504.    {
  3505.    long string = 0,   /* bytes in strings */
  3506.         text   = 0,   /* bytes in topic text (stored on disk) */
  3507.         data   = 0,   /* bytes in active data structure */
  3508.         dead   = 0;   /* bytes in unused data structure */
  3509.    int  ctr, ctr2;
  3510.  
  3511.    for (ctr=0; ctr<num_topic; ctr++)
  3512.       {
  3513.       data   += sizeof(TOPIC);
  3514.       string += topic[ctr].title_len;
  3515.       text   += topic[ctr].text_len;
  3516.       data   += topic[ctr].num_page * sizeof(PAGE);
  3517.  
  3518.       dead   += (PAGE_ALLOC_SIZE-(topic[ctr].num_page%PAGE_ALLOC_SIZE)) * sizeof(PAGE);
  3519.       }
  3520.  
  3521.    for (ctr=0; ctr<num_link; ctr++)
  3522.       {
  3523.       data += sizeof(LINK);
  3524.       string += strlen(a_link[ctr].name);
  3525.       }
  3526.  
  3527.    if (num_link > 0)
  3528.       dead += (LINK_ALLOC_SIZE-(num_link%LINK_ALLOC_SIZE)) * sizeof(LINK);
  3529.  
  3530.    for (ctr=0; ctr<num_label; ctr++)
  3531.       {
  3532.       data   += sizeof(LABEL);
  3533.       string += strlen(label[ctr].name) + 1;
  3534.       }
  3535.  
  3536.    if (num_label > 0)
  3537.       dead += (LABEL_ALLOC_SIZE-(num_label%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3538.  
  3539.    for (ctr=0; ctr<num_plabel; ctr++)
  3540.       {
  3541.       data   += sizeof(LABEL);
  3542.       string += strlen(plabel[ctr].name) + 1;
  3543.       }
  3544.  
  3545.    if (num_plabel > 0)
  3546.       dead += (LABEL_ALLOC_SIZE-(num_plabel%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3547.  
  3548.    for (ctr=0; ctr<num_contents; ctr++)
  3549.       {
  3550.       int t;
  3551.  
  3552.       t = ( MAX_CONTENT_TOPIC - contents[ctr].num_topic ) *
  3553.       ( sizeof(contents[0].is_label[0])   +
  3554.         sizeof(contents[0].topic_name[0]) +
  3555.         sizeof(contents[0].topic_num[0])     );
  3556.       data += sizeof(CONTENT) - t;
  3557.       dead += t;
  3558.       string += strlen(contents[ctr].id) + 1;
  3559.       string += strlen(contents[ctr].name) + 1;
  3560.       for (ctr2=0; ctr2<contents[ctr].num_topic; ctr2++)
  3561.      string += strlen(contents[ctr].topic_name[ctr2]) + 1;
  3562.       }
  3563.  
  3564.    dead += (CONTENTS_ALLOC_SIZE-(num_contents%CONTENTS_ALLOC_SIZE)) * sizeof(CONTENT);
  3565.  
  3566.    printf("\n");
  3567.    printf("Memory Usage:\n");
  3568.    printf("%8ld Bytes in buffers.\n", (long)BUFFER_SIZE);
  3569.    printf("%8ld Bytes in strings.\n", string);
  3570.    printf("%8ld Bytes in data.\n", data);
  3571.    printf("%8ld Bytes in dead space.\n", dead);
  3572.    printf("--------\n");
  3573.    printf("%8ld Bytes total.\n", (long)BUFFER_SIZE+string+data+dead);
  3574.    printf("\n");
  3575.    printf("Disk Usage:\n");
  3576.    printf("%8ld Bytes in topic text.\n", text);
  3577.    }
  3578.  
  3579.  
  3580. void report_stats(void)
  3581.    {
  3582.    int  pages = 0;
  3583.    int        t;
  3584.  
  3585.    for (t=0; t<num_topic; t++)
  3586.       pages += topic[t].num_page;
  3587.  
  3588.    printf("\n");
  3589.    printf("Statistics:\n");
  3590.    printf("%8d Topics\n", num_topic);
  3591.    printf("%8d Links\n", num_link);
  3592.    printf("%8d Labels\n", num_label);
  3593.    printf("%8d Private labels\n", num_plabel);
  3594.    printf("%8d Table of contents (DocContent) entries\n", num_contents);
  3595.    printf("%8d Online help pages\n", pages);
  3596.    printf("%8d Document pages\n", num_doc_pages);
  3597.    }
  3598.  
  3599.  
  3600. /*
  3601.  * add/delete help from .EXE functions.
  3602.  */
  3603.  
  3604.  
  3605. void add_hlp_to_exe(char *hlp_fname, char *exe_fname)
  3606.    {
  3607.    int                exe,   /* handles */
  3608.                 hlp;
  3609.    long             len,
  3610.                 count;
  3611.    int                size;
  3612.    struct help_sig_info hs;
  3613.  
  3614.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3615.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3616.  
  3617.    if ( (hlp=open(hlp_fname, O_RDONLY|O_BINARY)) == -1 )
  3618.       fatal(0,"Unable to open \"%s\"", hlp_fname);
  3619.  
  3620.    msg("Appending %s to %s", hlp_fname, exe_fname);
  3621.  
  3622.    /* first, check and see if any help is currently installed */
  3623.  
  3624.    lseek(exe, filelength(exe) - sizeof(struct help_sig_info), SEEK_SET);
  3625.  
  3626.    read(exe, (char *)&hs, 10);
  3627.  
  3628.    if ( hs.sig == HELP_SIG )
  3629.       warn(0,"Overwriting previous help. (Version=%d)", hs.version);
  3630.    else
  3631.       hs.base = filelength(exe);
  3632.  
  3633.    /* now, let's see if their help file is for real (and get the version) */
  3634.  
  3635.    read(hlp, (char *)&hs, sizeof(long)+sizeof(int));
  3636.  
  3637.    if (hs.sig != HELP_SIG )
  3638.       fatal(0,"Help signature not found in %s", hlp_fname);
  3639.  
  3640.    msg("Help file %s Version=%d", hlp_fname, hs.version);
  3641.  
  3642.    /* append the help stuff, overwriting old help (if any) */
  3643.  
  3644.    lseek(exe, hs.base, SEEK_SET);
  3645.  
  3646.    len = filelength(hlp) - sizeof(long) - sizeof(int); /* adjust for the file signature & version */
  3647.  
  3648.    for (count=0; count<len; )
  3649.       {
  3650.       size = (int) min((long)BUFFER_SIZE, len-count);
  3651.       read(hlp, buffer, size);
  3652.       write(exe, buffer, size);
  3653.       count += size;
  3654.       }
  3655.  
  3656.    /* add on the signature, version and offset */
  3657.  
  3658.    write(exe, (char *)&hs, 10);
  3659.  
  3660.    chsize(exe, lseek(exe,0L,SEEK_CUR));/* truncate if old help was longer */
  3661.  
  3662.    close(exe);
  3663.    close(hlp);
  3664.    }
  3665.  
  3666.  
  3667. void delete_hlp_from_exe(char *exe_fname)
  3668.    {
  3669.    int     exe;   /* file handle */
  3670.    struct help_sig_info hs;
  3671.  
  3672.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3673.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3674.  
  3675.    msg("Deleting help from %s", exe_fname);
  3676.  
  3677.    /* see if any help is currently installed */
  3678.  
  3679. #ifndef XFRACT
  3680.    lseek(exe, filelength(exe) - 10, SEEK_SET);
  3681.    read(exe, (char *)&hs, 10);
  3682. #else
  3683.    lseek(exe, filelength(exe) - 12, SEEK_SET);
  3684.    read(exe, (char *)&hs, 12);
  3685. #endif
  3686.  
  3687.    if ( hs.sig == HELP_SIG )
  3688.       {
  3689.       chsize(exe, hs.base);   /* truncate at the start of the help */
  3690.       close(exe);
  3691.       }
  3692.    else
  3693.       {
  3694.       close(exe);
  3695.       fatal(0,"No help found in %s", exe_fname);
  3696.       }
  3697.    }
  3698.  
  3699.  
  3700. /*
  3701.  * command-line parser, etc.
  3702.  */
  3703.  
  3704.  
  3705. #define MODE_COMPILE 1
  3706. #define MODE_PRINT   2
  3707. #define MODE_APPEND  3
  3708. #define MODE_DELETE  4
  3709.  
  3710.  
  3711. int main(int argc, char *argv[])
  3712.    {
  3713.    int      show_stats = 0,
  3714.       show_mem   = 0;
  3715.    int      mode         = 0;
  3716.  
  3717.    char **arg;
  3718.    char   fname1[81],
  3719.       fname2[81];
  3720.    char   swappath[81];
  3721.  
  3722.    fname1[0] = fname2[0] = swappath[0] = 0;
  3723.  
  3724.    printf("HC - FRACTINT Help Compiler.\n\n");
  3725.  
  3726.    buffer = malloc(BUFFER_SIZE);
  3727.  
  3728.    if (buffer == NULL)
  3729.       fatal(0,"Not enough memory to allocate buffer.");
  3730.  
  3731.    for (arg= &argv[1]; argc>1; argc--, arg++)
  3732.       {
  3733.       switch ( (*arg)[0] )
  3734.      {
  3735.      case '/':
  3736.      case '-':
  3737.         switch ( (*arg)[1] )
  3738.            {
  3739.            case 'c':
  3740.           if (mode == 0)
  3741.              mode = MODE_COMPILE;
  3742.           else
  3743.              fatal(0,"Cannot have /c with /a, /d or /p");
  3744.           break;
  3745.  
  3746.            case 'a':
  3747.           if (mode == 0)
  3748.              mode = MODE_APPEND;
  3749.           else
  3750.              fatal(0,"Cannot have /a with /c, /d or /p");
  3751.           break;
  3752.  
  3753.            case 'd':
  3754.           if (mode == 0)
  3755.              mode = MODE_DELETE;
  3756.           else
  3757.              fatal(0,"Cannot have /d with /c, /a or /p");
  3758.           break;
  3759.  
  3760.            case 'p':
  3761.           if (mode == 0)
  3762.              mode = MODE_PRINT;
  3763.           else
  3764.              fatal(0,"Cannot have /p with /c, /a or /d");
  3765.           break;
  3766.  
  3767.            case 'm':
  3768.           if (mode == MODE_COMPILE)
  3769.              show_mem = 1;
  3770.           else
  3771.              fatal(0,"/m switch allowed only when compiling (/c)");
  3772.           break;
  3773.  
  3774.            case 's':
  3775.           if (mode == MODE_COMPILE)
  3776.              show_stats = 1;
  3777.           else
  3778.              fatal(0,"/s switch allowed only when compiling (/c)");
  3779.           break;
  3780.  
  3781.            case 'r':
  3782.           if (mode == MODE_COMPILE || mode == MODE_PRINT)
  3783.              strcpy(swappath, (*arg)+2);
  3784.           else
  3785.              fatal(0,"/r switch allowed when compiling (/c) or printing (/p)");
  3786.           break;
  3787.  
  3788.            case 'q':
  3789.           quiet_mode = 1;
  3790.           break;
  3791.  
  3792.            default:
  3793.           fatal(0,"Bad command-line switch /%c", (*arg)[1]);
  3794.           break;
  3795.            }
  3796.         break;
  3797.  
  3798.      default:   /* assume it is a fname */
  3799.         if (fname1[0] == '\0')
  3800.            strcpy(fname1, *arg);
  3801.         else if (fname2[0] == '\0')
  3802.            strcpy(fname2, *arg);
  3803.         else
  3804.            fatal(0,"Unexpected command-line argument \"%s\"", *arg);
  3805.         break;
  3806.      } /* switch */
  3807.       } /* for */
  3808.  
  3809.    strupr(fname1);
  3810.    strupr(fname2);
  3811.    strupr(swappath);
  3812.  
  3813.    switch (mode)
  3814.       {
  3815.       case 0:
  3816.          printf( "To compile a .SRC file:\n");
  3817.          printf( "      HC /c [/s] [/m] [/r[path]] [src_file]\n");
  3818.          printf( "         /s       = report statistics.\n");
  3819.          printf( "         /m       = report memory usage.\n");
  3820.          printf( "         /r[path] = set swap file path.\n");
  3821.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3822.          printf( "To print a .SRC file:\n");
  3823.          printf( "      HC /p [/r[path]] [src_file] [out_file]\n");
  3824.          printf( "         /r[path] = set swap file path.\n");
  3825.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3826.          printf( "         out_file = Filename to print to. Default is \"%s\"\n",
  3827.          DEFAULT_DOC_FNAME);
  3828.          printf( "To append a .HLP file to an .EXE file:\n");
  3829.          printf( "      HC /a [hlp_file] [exe_file]\n");
  3830.          printf( "         hlp_file = .HLP file.  Default is \"%s\"\n", DEFAULT_HLP_FNAME);
  3831.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3832.          printf( "To delete help info from an .EXE file:\n");
  3833.          printf( "      HC /d [exe_file]\n");
  3834.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3835.          printf( "\n");
  3836.          printf( "Use \"/q\" for quiet mode. (No status messages.)\n");
  3837.      break;
  3838.  
  3839.       case MODE_COMPILE:
  3840.      if (fname2[0] != '\0')
  3841.         fatal(0,"Unexpected command-line argument \"%s\"", fname2);
  3842.  
  3843.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3844.  
  3845.      strcat(swappath, SWAP_FNAME);
  3846.  
  3847.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3848.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3849.      swappos = 0;
  3850.  
  3851.      read_src(src_fname);
  3852.  
  3853.      if (hdr_fname[0] == '\0')
  3854.         error(0,"No .H file defined.  (Use \"~HdrFile=\")");
  3855.      if (hlp_fname[0] == '\0')
  3856.         error(0,"No .HLP file defined.  (Use \"~HlpFile=\")");
  3857.      if (version == -1)
  3858.         warn(0,"No help version has been defined.  (Use \"~Version=\")");
  3859.  
  3860.      /* order of these is very important... */
  3861.  
  3862.      make_hot_links();  /* do even if errors since it may report */
  3863.                 /* more... */
  3864.  
  3865.      if ( !errors )     paginate_online();
  3866.      if ( !errors )     paginate_document();
  3867.      if ( !errors )     calc_offsets();
  3868.      if ( !errors )     sort_labels();
  3869.      if ( !errors )     write_hdr(hdr_fname);
  3870.      if ( !errors )     write_help(hlp_fname);
  3871.  
  3872.      if ( show_stats )
  3873.         report_stats();
  3874.  
  3875.      if ( show_mem )
  3876.         report_memory();
  3877.  
  3878.      if ( errors || warnings )
  3879.         report_errors();
  3880.  
  3881.      fclose(swapfile);
  3882.      remove(swappath);
  3883.  
  3884.      break;
  3885.  
  3886.       case MODE_PRINT:
  3887.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3888.  
  3889.      strcat(swappath, SWAP_FNAME);
  3890.  
  3891.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3892.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3893.      swappos = 0;
  3894.  
  3895.      read_src(src_fname);
  3896.  
  3897.      make_hot_links();
  3898.  
  3899.      if ( !errors )     paginate_document();
  3900.      if ( !errors )     print_document( (fname2[0]=='\0') ? DEFAULT_DOC_FNAME : fname2 );
  3901.  
  3902.      if ( errors || warnings )
  3903.         report_errors();
  3904.  
  3905.      fclose(swapfile);
  3906.      remove(swappath);
  3907.  
  3908.      break;
  3909.  
  3910.       case MODE_APPEND:
  3911.      add_hlp_to_exe( (fname1[0]=='\0') ? DEFAULT_HLP_FNAME : fname1,
  3912.              (fname2[0]=='\0') ? DEFAULT_EXE_FNAME : fname2);
  3913.      break;
  3914.  
  3915.       case MODE_DELETE:
  3916.      if (fname2[0] != '\0')
  3917.         fatal(0,"Unexpected argument \"%s\"", fname2);
  3918.      delete_hlp_from_exe((fname1[0]=='\0') ? DEFAULT_EXE_FNAME : fname1);
  3919.      break;
  3920.       }
  3921.  
  3922.    free(buffer);
  3923.  
  3924.    return ( errors );   /* return the number of errors */
  3925.    }
  3926.  
  3927.  
  3928.